Новые монстры Введение Многие часто сталкиваются с тем, что при попытке добавления монстров не попавшими в финальную версию игра может вылетать. Попробую рассказать, как это дело можно исправить. Для примера возьму кошек (уж очень они мне нравятся) Добавить их можно отредактировать файл all.spawn, либо динамически создать в игре через функцию alife:create(). Вылет при появлении Обычно сопровождается сообщением в логе: Arguments : Can't open section 'cat_weak' Это значит, что монстр у нас не до конца описан. В файле конфигурации идет секция [m_cat_e]:monster_base Но для создания разных по силе конфигураций монстров обычно используется такая штука. Создается секция вида [cat_weak]:m_cat_e то есть ссылка на основную конфигурацию, а потом указываются параметры, которые отличаются от нее. Например сила, здоровье, угол обзора и тд. Мы пока ограничимся просто добавлением этой секции. В файле config\creatures\m_cat.ltx в конце допишем [cat_weak]:m_cat_e. Запускаем, добавляем кошку, вылетаем с ошибкой: Arguments : Can't find variable smart_terrain_choose_interval in [cat_weak] Что ж, добавим и этот параметр. Посмотрим, какой он у собак, и сделаем также: smart_terrain_choose_interval = 00:15:00 Запускаем, вылетаем с ошибкой: Arguments : Can't find variable satiety_threshold in [cat_weak] Вобщем, добавляем параметры, по кошка не станет нормально появлятся. Такие ошибки легко локализуются и исправляются. Ну, на всякий случай, нужно добавить еще две строчки: critical_wound_threshold = 0.4 critical_wound_decrease_quant = 0. После этого кошка появляется, бегает, с 30 метров бежит нас кушать. Мы этого не хотим, стреляем в нее, и у нас происходит Вылет при убийстве Это знаменитая ошибка: Arguments : LUA error: e:\stalker\gamedata\scripts\_g.script:20 ........ Самое просто и неправильное решение - закоментировать 20-ю строку скрипта _g.script, вылет исчезнет, но ошибка никуда не денется. Я не могу сказать с уверенностью, к чему игнорирование этого факта может привести, так как исправлял причину, а не следствие. Путем нехитрых логических размышлений решение было найдено. Вернее тупым перебором всего, что относилось к делу, криками на форумах, битьем головой о стенку... Потом пришло озарение, вместе с сакраментальным вопросом "что происходит после смерти?" А происходит добавление в статистику убитого объекта (монстра, нпс-а). Поэтому лезем в скрипт xr_statistic.script. В самом начале видим объявление переменной local killCountProps = {...} в которой кошек нет. Поэтому добавляем их туда (например, в конец), для начисления очков опыта: cat_weak = 1, cat_normal = 2, cat_strong = 3, Хотя мы сделали только cat_weak, но на будущее добавим и остальных. Затем идет перечисление используемых классов монстров: monster_classes = { кошек нет и здесь, добавляем: [clsid.cat_s] = "cat", Смотрим дальше, видим функцию addKillCount(npc), которая определяет сообщество (community) убитого. Функция вызывает getNpcType(npc), которая находится в этом же скрипте. А там идет обращение к функции IsStalker(npc), и возвращает сообщество и ранг убиенного. Но тут про мостров ни слова, а значит идем дальше. Функция IsStalker(npc) обнаруживается в скрипте _g.script. Просматривая его обнаруживаем следующие интересующие нас функции: • is_object_monster(obj) кошек нет, добавляем рядом с собаками: otype == clsid.cat or • IsMonster (object, class_id) кошки есть, ничего не трогаем. Для страховки проверим еще одни файл, class_registrator.script. Находим в нем строку cs_register (object_factory, "CCat", "se_monster.se_monster", "SM_CAT_S", "cat_s") и успокаиваемся на этом. Запускаем, убиваем, подходим, пытаемся обыскать - вылетаем. Потому что случился Вылет при обыске Ну тут уже проще. Вспоминаем, что у нас показывается в инвентаре убитого? Иконка растерзанного трупа. Проверяем m_cat.ltx - действительно, параметр icon отсутствует. Тут появляется два пути. Первый - нарисовать новую иконку, вставить ее в файл ui_npc_monster.dds, определить координаты и добавить их в xml-описатель ui_npc_monster.xml. И так для каждого нового монстра. Второй - использовать общую иконку для всех монстров, у которых отсутствует этот параметр. Мы им и воспользуемся, так как для тестовых целей сойдет, а для серьезного применения все равно необходимо иметь хоть какие-то художественные навыки (если кто видел мой тестовый мод "15 кошек", то помнят тот ужас вместо иконки кошки ) Для этого отредактируем файл defines.ltx , так как именно в нём содержится секция monster_base, общая для всех монстров. Открываем, ищем секцию, и вставляем icon = ui_frame_error_sign_red Это будет такой красный крест в круге. Запускаем, убиваем, обыскиваем и не вылетаем. Итак, свежедобавленный монстр бегает, живет, нападает. Однако та же кошка по умолчанию невероятно сильна, убивает с одного удара, поэтому для нормальной жизни необходимо подредактировать секцию атаки. Внимание!!! Огромный минус в том, что убитое животное не попадает в статистику, хотя мы вроде все для этого сделали. С этим предлагаю разобраться самим. Подсказка: в энциклопедии кошек тоже нет
Добавлено (31.07.2011, 12:05:56) --------------------------------------------- так идем дальше [spoiler]
В ТЧ есть единственный момент, когда в результате диалога с НПС, ГГ перемещается. Это диалог с "О-Сознанием". После отказа от вступления, ГГ перемещается на АЭС2. Если использовать эту функцию, можно создать в ТЧ проводника. Примечание: Проводник не будет перемещаться вместе с вами, как в ЗП. Вы будете перемещаться в одиночку, как в ЧН.
Практика
Для этого: 1). Для этого в gamedata\scripts находим файл dialogs_aes.script. В нём имеется следующая функция:
function osoznanie_decline(npc, actor) db.actor:disable_info_portion("oso_init_dialog") npc:stop_talk() actor:stop_talk() xr_effects.enable_ui(db.actor, nil) local point = patrol("mon_jump_aes2_walk") local look = patrol("mon_jump_aes2_look")
db.actor:set_actor_position(point:point(0)) local dir = look:point(0):sub(point:point(0)) db.actor:set_actor_direction(-dir:getH())
end
2). Копируем эту функцию, допустим, в escape_dialog из директории gamedata\scripts 3). Обратите внимание на вот этот фрагмент из функции: db.actor:disable_info_portion("oso_init_dialog"). Здесь функция ссылается на инфопоршень, но на Кордоне данный инфопоршень не прописан. 4). Тогда переименовываем данный инфопоршень так, как вам удобно, например db.actor:disable_info_portion("esc_init_dialog"). Примечание: Название инфопоршня это "esc_init_dialog" (то ,что за скобками, переименовывать не надо). Затем добавляем инфопоршень в файл info_101escape.xml из директории gamedata\config\gameplay. В этом файле добавляем следующее: 5). Итак, функция есть, инфопоршень тоже есть. Теперь нужны координаты. Обратите внимание на следующий фрагмент функции:
local point = patrol("mon_jump_aes2_walk") local look = patrol("mon_jump_aes2_look")
Здесь функция ссылается на определённые координаты на АЭС2. Но нам нужны координаты на Кордоне. Для этого открываем way_101_escape.ltx и там дописываем пути. Например вот так:
[spoiler]Как изменить параметры конкретного оружия? Параметры стволов заданы в конфигурационных файлах по адресу gamedata\configs\weapons Возьмем, для примера, конфиг w_g36.ltx. Он делится на разделы: Первый раздел Здесь хранится общая информация о оружии - его класс, тип, ссылки на спавн и т.д. Нас интересуют следующие строки: description = enc_weapons1_wpn-g36 - ссылка на string_id, из которого игра подгружает описание этого оружия. ef_main_weapon_type = 2 - основной тип, к которому принадлежит оружие ef_weapon_type = 6 - подтип, к которому принадлежит оружие Второй раздел Содержит модификаторы, которые даются к углу зрения/дальности обзора NPC, держащего это оружие в руках: holder_range_modifier = 1.85 - во сколько раз увеличивается eye_range holder_fov_modifier = 0.3 - во сколько раз увеличивается eye_fov Третий раздел Содержит данные о самом оружии, его некоторых характеристиках: cost = 18000 - базовая цена (торговцы умножают её на некоторый коэффициент) weapon_class = assault_rifle - класс оружия (здесь - штурмовая винтовка) ammo_mag_size = 30 - размер магазина ammo_class = ammo_5.56x45_ss190, ammo_5.56x45_ap - типы используемых патронов grenade_class = ammo_m209 - тип используемых гранат fire_modes = 1, -1 - режимы ведения огня hand_dependence = 1 - засивимость о рук (?) - возможно, речь идет о качании ствола single_handed = 0 - держится ли только в одной руке slot = 2 - слот в инвентаре animation_slot = 2 - вид анимации (для пистолета/для винтовки) inv_name = wpn-g36 - ссылка на имя, отображаемое в инвентаре, тоже берется из string table, как и описание inv_name_short = wpn-g36 - короткое имя; в данном случае используется то же самое inv_weight = 3.6 - вес inv_grid_width = 5 - координаты первого угла иконки по x inv_grid_height = 2 - координаты первого угла иконки по y inv_grid_x = 0 - координаты второго угла иконки по x inv_grid_y = 10 - координаты второго угла иконки по y Четвертый раздел В четвертом разделе хранится информация о износе/отдаче оружия. Практически все параметры там снабжены комментариями, поэтому приведу лишь самые интересные: cam_relax_speed = 5.7 - скорость возврата в исходное положение cam_dispersion = 0.2 - увеличения угла (в градусах) с каждым выстрелом fire_dispersion_condition_factor = 5 - увеличение дисперсии в процентах при максимальном износе misfire_probability = 0.003 - вероятность осечки при максимальном износе misfire_condition_k = 0.05 - порог (в данном случае - 5%), после которого оружие может заклинивать condition_shot_dec = 0.0002 - увеличение износа при каждом выстреле Пятый раздел Здесь хранится множество параметров, из которых наиболее интересны эти: PDM_disp_base = 3.0 - множитель, на который умножается базовая дисперсия оружия, когда ГГ (Главный Герой) стоит на месте в полный рост; PDM_disp_vel_factor = 1.3 - множитель, на который умножается базовая дисперсия оружия, когда ГГ крутит оружием или бежит; PDM_disp_accel_factor = 1.3 - множитель, на который умножается базовая дисперсия оружия, когда ГГ бежит в спринте; PDM_crouch = 1.0 - множитель, на который умножается базовая дисперсия оружия, когда ГГ идет пригнувшись; PDM_crouch_no_acc = 1.0 - множитель, на который умножается базовая дисперсия оружия, когда ГГ стоит на месте, пригнувшись; hit_power = 0.50, 0.54, 0.57, 0.60 - сила выстрела hit_impulse = 105 - импульс пули (сила, которую летящая пуля передает жертве, влияет на поведение ragdoll-тела) hit_type = fire_wound - тип причиняемых повреждений, в данном случае - пулевые ранения (параметр в синглплеере ни на что не влияет) fire_distance = 600 - максимальная дистанция для выстрела bullet_speed = 925 - начальная скорость пули hud = wpn_g36_hud - внешний вид оружия Шестой раздел position = -0.026, -0.132, 0.0 - позиция по отношению к игроку (?) orientation = 0, 0, 0 - направление, в которое смотрит ствол (?) Седьмой раздел Содержит описания визуальной стороны оружия и некоторые другие параметры: startup_ammo = 90 - как нетрудно догадаться, стартовое количество патронов (в синглплеере ни на что не влияет) visual = weapons\g36\wpn_g36.ogf - модель оружия, используемая NPC, а также игроком при виде от третьего лица ph_mass = 4 - физическая масса, используемая при расчетах scope_status = 1 - ситуация со съемным прицелом silencer_status = 0 - ситуация со съемным глушителем grenade_launcher_status = 0 - ситуация с подствольным гранатометом Параметры: 0 - нет, новый прикрепить нельзя 1 - уже есть, несъемный 2 - нет, но можно установить новый zoom_enabled = true - есть ли зум (прицеливание) scope_zoom_factor = 33.3 - какой зум дает прицеливание (здесь - 1.8х) scope_texture = wpn\wpn_crosshair_g36 - текстура прицельной сетки shell_point = 0.15, 0.0, -0.05 - точка вылета гильз shell_dir = 0.0, 1.0, 0.0 fire_point = -0.000000,0.062000,0.134000 - точка выстрела fire_point2 = 0.30, 0.00, 0.05 - точка выстрела (2) fire_bone = wpn_body orientation = 0, 0, 0 - направление position = 0, 0, 0 - позиция visual = weapons\g36\wpn_g36_hud.ogf - модель, отображаемая у нас в руках Изменение описаний Описания оружия хранятся в файле: ...\S.T.A.L.K.E.R\gamedata\config\text\rus\string_table_enc_weapons.xml В нем хранятся строки с названиями и описаниями, на которые ссылаются конфиги оружия. Например, тот же G36 ссылается сюда: [...] <string id="enc_weapons1_wpn-g36"> <text>Штурмовая винтовка немецкого производства, представляющая собой первоклассный образец современного оружия - лёгкого, надёжного и эргономичного.\n Боеприпасы:\n обычный 5,56x45 мм SS109,\n бронебойный 5,56x45 мм АР.</text> [...] <string id="wpn-g36"> <text>ГП37</text> [...]Меняя их содержимое, мы меняем описания/названия данного оружия.
Добавлено (15.08.2011, 21:31:42) --------------------------------------------- [Новые монстры] Введение Многие часто сталкиваются с тем, что при попытке добавления монстров не попавшими в финальную версию игра может вылетать. Попробую рассказать, как это дело можно исправить. Для примера возьму кошек (уж очень они мне нравятся) Добавить их можно отредактировать файл all.spawn, либо динамически создать в игре через функцию alife:create(). Вылет при появлении Обычно сопровождается сообщением в логе: Arguments : Can't open section 'cat_weak' Это значит, что монстр у нас не до конца описан. В файле конфигурации идет секция [m_cat_e]:monster_base Но для создания разных по силе конфигураций монстров обычно используется такая штука. Создается секция вида [cat_weak]:m_cat_e то есть ссылка на основную конфигурацию, а потом указываются параметры, которые отличаются от нее. Например сила, здоровье, угол обзора и тд. Мы пока ограничимся просто добавлением этой секции. В файле config\creatures\m_cat.ltx в конце допишем [cat_weak]:m_cat_e. Запускаем, добавляем кошку, вылетаем с ошибкой: Arguments : Can't find variable smart_terrain_choose_interval in [cat_weak] Что ж, добавим и этот параметр. Посмотрим, какой он у собак, и сделаем также: smart_terrain_choose_interval = 00:15:00 Запускаем, вылетаем с ошибкой: Arguments : Can't find variable satiety_threshold in [cat_weak] Вобщем, добавляем параметры, по кошка не станет нормально появлятся. Такие ошибки легко локализуются и исправляются. Ну, на всякий случай, нужно добавить еще две строчки: critical_wound_threshold = 0.4 critical_wound_decrease_quant = 0. После этого кошка появляется, бегает, с 30 метров бежит нас кушать. Мы этого не хотим, стреляем в нее, и у нас происходит Вылет при убийстве Это знаменитая ошибка: Arguments : LUA error: e:\stalker\gamedata\scripts\_g.script:20 ........ Самое просто и неправильное решение - закоментировать 20-ю строку скрипта _g.script, вылет исчезнет, но ошибка никуда не денется. Я не могу сказать с уверенностью, к чему игнорирование этого факта может привести, так как исправлял причину, а не следствие. Путем нехитрых логических размышлений решение было найдено. Вернее тупым перебором всего, что относилось к делу, криками на форумах, битьем головой о стенку... Потом пришло озарение, вместе с сакраментальным вопросом "что происходит после смерти?" А происходит добавление в статистику убитого объекта (монстра, нпс-а). Поэтому лезем в скрипт xr_statistic.script. В самом начале видим объявление переменной local killCountProps = {...} в которой кошек нет. Поэтому добавляем их туда (например, в конец), для начисления очков опыта: cat_weak = 1, cat_normal = 2, cat_strong = 3, Хотя мы сделали только cat_weak, но на будущее добавим и остальных. Затем идет перечисление используемых классов монстров: monster_classes = { кошек нет и здесь, добавляем: [clsid.cat_s] = "cat", Смотрим дальше, видим функцию addKillCount(npc), которая определяет сообщество (community) убитого. Функция вызывает getNpcType(npc), которая находится в этом же скрипте. А там идет обращение к функции IsStalker(npc), и возвращает сообщество и ранг убиенного. Но тут про мостров ни слова, а значит идем дальше. Функция IsStalker(npc) обнаруживается в скрипте _g.script. Просматривая его обнаруживаем следующие интересующие нас функции: • is_object_monster(obj) кошек нет, добавляем рядом с собаками: otype == clsid.cat or • IsMonster (object, class_id) кошки есть, ничего не трогаем. Для страховки проверим еще одни файл, class_registrator.script. Находим в нем строку cs_register (object_factory, "CCat", "se_monster.se_monster", "SM_CAT_S", "cat_s") и успокаиваемся на этом. Запускаем, убиваем, подходим, пытаемся обыскать - вылетаем. Потому что случился Вылет при обыске Ну тут уже проще. Вспоминаем, что у нас показывается в инвентаре убитого? Иконка растерзанного трупа. Проверяем m_cat.ltx - действительно, параметр icon отсутствует. Тут появляется два пути. Первый - нарисовать новую иконку, вставить ее в файл ui_npc_monster.dds, определить координаты и добавить их в xml-описатель ui_npc_monster.xml. И так для каждого нового монстра. Второй - использовать общую иконку для всех монстров, у которых отсутствует этот параметр. Мы им и воспользуемся, так как для тестовых целей сойдет, а для серьезного применения все равно необходимо иметь хоть какие-то художественные навыки (если кто видел мой тестовый мод "15 кошек", то помнят тот ужас вместо иконки кошки ) Для этого отредактируем файл defines.ltx , так как именно в нём содержится секция monster_base, общая для всех монстров. Открываем, ищем секцию, и вставляем icon = ui_frame_error_sign_red Это будет такой красный крест в круге. Запускаем, убиваем, обыскиваем и не вылетаем. Итак, свежедобавленный монстр бегает, живет, нападает. Однако та же кошка по умолчанию невероятно сильна, убивает с одного удара, поэтому для нормальной жизни необходимо подредактировать секцию атаки. Внимание!!! Огромный минус в том, что убитое животное не попадает в статистику, хотя мы вроде все для этого сделали. С этим предлагаю разобраться самим. Подсказка: в энциклопедии кошек тоже нет
Создание "перемещающихся" аномалий Небольшое вступление "Перемещающиеся" аномалии с самого момента релиза очень заинтересовали коммьюнити. Выдвигалось множество предположений о принципе их работы. На самом деле, всё сделано достаточно незамысловато. В свойствах аномалий прописываются параметры, в которых указывается в какой промежуток времени они будут активны. Таким образом создаются целые цепочки из аномалий, параметры которых взаимосвязаны. В оригинале всего две таких цепочки: в туннеле на Кордоне и в подземелье Агропрома. Описание параметров Всего используются три параметра: enabled_time - через какое время после начала отсчёта аномалия включится. disabled_time - через какое время после начала отсчёта аномалия отключится. start_time_shift - на сколько сдвинется момент начала отсчёта времени. (Использовать необязательно) После отключения аномалии процесс повторяется. Следующее включение произойдёт через промежуток времени прописанный в disable_time. В Level Editor'е эти параметры находятся в свойствах аномалии. При использовании ACDC параметры прописываются "в теле" нужной аномалии. Значения указываются в секундах. Небольшая наглядность Создадим "перемещающуюся" аномалию, которая будет менять своё местоположение каждые четыре секунды. Для этого создадим несколько аномалий (для примера взято 3 штуки). Для первой аномалии пропишем: enabled_time = 4 disabled_time = 8 start_time_shift = 8 Для второй: enabled_time = 4 disabled_time = 8 start_time_shift = 4 Для третьей: enabled_time = 4 disabled_time = 8 Таким образом, первой будет активироваться третья аномалия, работать четыре секунды и деактивироваться. Сразу после неё будет активироваться вторая аномалия, работать 4 секунды, ну и так далее... Вот так и создаётся ощущение того, что аномалия перемещается. Конечно, это только один из примеров использования этих параметров. Обладая должным желанием и терпением, можно делать целые аномальные поля, где каждая аномалия будет вести себя независимо от других, но вместе они будут образовывать смертельно опасное место.
Inventory new.xml Оформление инвентаря: По пути gamedata.db0/config/ui/ лежит файл inventory_new.xml содержащий координаты объектов инвентаря. Инвентарные слоты состоят из багграундных текстур, и слотовых сеток. К примеру строки <belt_slots x="0" y="80" width="1024" height="172"> <texture>ui_slots_belt</texture> </belt_slots> содержат координаты (x="0" y="80") и размер (width="1024" height="172") багграундной текстурки ui_slots_belt для двух оружейных слотов и слота ремня.
А строки <dragdrop_belt x="645" y="136" width="410" height="60" cell_width = "60" cell_height="60" rows_num="1" cols_num="5"/> содержат координаты (x="645" y="136"), размер сетки(width="410" height="60"), размер одной ячейки сетки (cell_width = "60" cell_height="60"), число строк (rows_num="1") и столбцов (cols_num="5") слотовой сетки инвентаря. Отсчет координат x y идет с левого верхнего угла. Меняя значения приведенных значения можно изменять внешний вид инвентаря. ________________________________________ Примеры: Изменив значения в строках <dragdrop_belt x="645" y="136" width="410" height="60" cell_width = "60" cell_height="60" rows_num="1" cols_num="5"/> на <dragdrop_belt x="590" y="118" width="410" height="104" cell_width = "52" cell_height="52" rows_num="2" cols_num="5"/>
у нас в ременном отделении под артефакты вместо 5ти, будет 10ть слотовых ячеек. Или изменив координаты (x="870" y="725") в строках <exit_button x="870" y="725" width="157" height="48"> .... .... </exit_button> на координаты (x="10" y="10"), можно перенести кнопку Выхода(из инвентаря) из правого нижнего угла в левый верхний угол.
Добавлено (16.08.2011, 04:12:23) --------------------------------------------- [spoiler тык]Сначала создадим тестовый скрипт например kfs.script, дальше открываем его блокнотом или другим текстовым редактором пишем: function main()-стока отвечающая за включение скрипта. spawn_npc() строка отвечающая за включения функции. Ну и сама функция: local spawn_point1 = vector():set(-226.2, -20.3, -158.4) local obj = alife():create("esc_bandit_respawn_1",spawn_point1,28485, 48) Позиция где будет стоять npc, а именно на кордоне чтобы узнать позицию нужно воспользоваться программой S.T.A.L.K.E.R. Position Informer (можно скачать у нас в файлах). local spawn_point1 = vector():set(-226.2, -20.3, -158.4) Что мы именно будем спавнить в данном случае заспавним бандита имена npc можно узнать в spawn_sections.ltx local obj = alife():create("esc_bandit_respawn_1",spawn_point1,20579,80) Геймес вертекс отвечает за то где будет распологаться npc в нашем случае на кордоне, можно так же узнать через S.T.A.L.K.E.R. Position Informer local obj = alife():create("esc_bandit_respawn_1",spawn_point1,28485, 48) Вот как должно получиться: function main() spawn_npc() end function spawn_npc() -- тест на кордоне в лагере навичков local spawn_point1 = vector():set(-226.2, -20.3, -158.4) local obj = alife():create("esc_bandit_respawn_1",spawn_point1,28485, 48) end Теперь необходимо включить наш скрипт заходим в bind_stalker.script где расположено: death_manager.init_drop_settings() Под ней вводим нашу функцию kfs.main() где kfs это имя скрипта который мы назвали, так всё теперь запускаем игру и смотрим. Что бы расположить сразу несколько npc или монстров просто под сторчкой делaйте несколькo копий вот так: local obj = alife():create("esc_bandit_respawn_1",spawn_point1,28485, 48) local obj = alife():create("esc_bandit_respawn_1",spawn_point1,28485, 48) local obj = alife():create("esc_bandit_respawn_1",spawn_point1,28485, 48) local obj = alife():create("esc_bandit_respawn_2",spawn_point1,28485, 48) local obj = alife():create("esc_bandit_respawn_2",spawn_point1,28485, 48)
СКРИПТ ДЛЯ СПАВНА ЛЮБЫХ НАБОРОВ ОБЪЕКТОВ
Приведенный ниже скрипт является общей функцией, куда передаются данные. Его можно положить в любой скриптовый файл. Если же хотите, что бы функцию можно было вызывать также и из логики - то скрипт нужно положить в файл xr_effects.script.
Вызов соответственно нужно делать именно функции с данными - любым удобным\подходящим по ситуации способом - через экшен в диалоге, через другой скрипт, через файл info_portions (если хотите заспавнить, скажем, эктору что-то в инвентарь при старте игры).
Для каждого объекта надо указать кол-во: если нужен один объект то 1, если сто - то 100 и т.д.
Общая функция Код: function SpawnToNpcOrToLocation(tab_items,tab_where) local count = 0 local pos, lvid, gvid, id, obj
local function Compare(id) obj = alife():object(id) return obj and obj.profile_name and obj:profile_name() == tab_where end
for k, v in pairs(tab_items) do if type(tab_where)=='table' then for k, v in pairs (tab_items[k]) do count = k for i=1, count do alife():create(v,vector():set(tab_where['x'], tab_where['y'],tab_where['z']), tab_where['lv'],tab_where['gv']) end end
elseif type(tab_where)=='string' then if tab_where =="actor" then obj = alife():actor() else for i=1, 65534 do local predicate = Compare(i) if predicate then break end end end
if obj == nil then return end for k, v in pairs (tab_items[k]) do count = k for i=1, count do alife():create(v, obj.position, obj.m_level_vertex_id, obj.m_game_vertex_id, obj.id) end end
elseif type(tab_where)=='number' then obj = level_object_by_sid(tab_where) if obj ~= nil then id, pos, lvid, gvid= obj:id(), obj:position(), obj:level_vertex_id(), obj:game_vertex_id() elseif obj == nil then obj = alife():story_object(tab_where) id, pos, lvid, gvid = obj.id, obj.position, obj.m_level_vertex_id, obj.m_game_vertex_id for k, v in pairs (tab_items[k]) do count = k for i=1, count do alife():create(v, pos, lvid, gvid, id) end end end end end end
Функции с передачей данных:
--для спавна по координатам:
function uny_spawn_1() local tab_items ={ {[1]="exo_outfit"}, {[1]="af_cristall_flower"}, {[1]="wpn_fn2000"}, {[5]="ammo_5.56x45_ss190"}, {[3]="bread"}, {[5]="medkit_army"}} local tab_where ={x=-22,y=-33,z=-66,lv=33333,gv=44} this.SpawnToNpcOrToLocation(tab_items,tab_where) end
--для спавна в инвентарь эктора:
function uny_spawn_2() local tab_items ={ {[1]="exo_outfit"}, {[1]="af_cristall_flower"}, {[1]="wpn_fn2000"}, {[5]="ammo_5.56x45_ss190"}, {[3]="bread"}, {[5]="medkit_army"}} local tab_where = "actor" this.SpawnToNpcOrToLocation(tab_items,tab_where) end
--для спавна в инвентарь NPC по его sid'у:
function uny_spawn_3() local tab_items ={ {[1]="exo_outfit"}, {[2]="af_cristall_flower"}, {[1]="wpn_fn2000"}, {[5]="ammo_5.56x45_ss190"}, {[3]="bread"}, {[5]="medkit_army"}} local tab_where = 006 -- это sid волка this.SpawnToNpcOrToLocation(tab_items,tab_where) end
Доработанный вариант дает возможностью спавнить по сиду как клиентским так и серверным, то есть находящимся в оффлайне объектам, а также есть возможность спавнить NPC по их профильному имени, которое указывается в теге <specific_character id = в файлах character_desc_локация и в файле npc_profile в теге <character id =
-- для спавна в инвентарь NPC по его профильному имени
function uny_spawn_4() local tab_items ={ {[1]="ammo_12x70_buck"}, {[1]="ammo_12x76_dart"}, {[1]="ammo_12x76_zhekan"}, {[1]="wpn_toz34"}, {[1]="wpn_ak74u"}, {[1]="stalker_outfit"}, {[1]="mutant_zombie_hand"}, {[1]="mutant_snork_leg"} } local tab_where = "mil_Svoboda_trader" -- имя из профиля this.SpawnToNpcOrToLocation(tab_items,tab_where) end
Добавлено (16.08.2011, 04:13:39) --------------------------------------------- [spoiler]ПОСТЭФФЕКТЫ И АНИМАЦИИ КАМЕРЫ
Часть первая.
Что в имени тебе моем?
Делать полный разбор всего содержимого папки anims я разумеется не буду. Рассмотрим некоторые аспекты. В этой папке находятся файлы содержащие набор ключей для запуска того или иного эффекта. В корневой папке в основном находятся файлы постпроцессинга – с расширением ppe. В подпапке camera_effects - эффекты камеры или анимации. Какая между ними разница? Так как я не спец по 3D графике, то скажу просто - постэффект (или правильнее потспроцессинг) это такой шейдерный фильтр, который накладывается на текстуры игры, создавая цветовое изменение картинки. Простой пример – постэффект teleport.ppe. При срабатывании телепорта, что мы видим? Правильно – белый свет, который делает экран полностью белым на несколько секунд. Или nightvision.ppe – активируется при включении прибора ночного видения. В игре их несколько. Эффект думаю вам знаком.
Собственно файлы постпроцессинга существует в игре в текстовом и компилированом виде. В текстовом - в файлах по пути gamedata\config\scripts - например .earthshake.ltx или teleport.ltx В них в числовом коде задан набор правил, как должен изменяться тот или иной параметр изображения эффекта во времени. Всего этих параметров - 16. Полный их разбор вы можете прочитать в статье "Формат файлов постпроцессинга" на stalkerin.gameru.net. В том же .earthshake.ltx. - задано всего два параметра duality_h. и duality_v. - раздвоение изображения по горизонтали и по вертикали. Тем самым мы имеем возможность вручную изменить эти параметры и сделать тот или эффект другим. Но здесь мы этого разбирать не будем, так как займемся не текстовыми, а компилированными файлами постпроцессинга из папки anims.
radar_psi.ppe - дает небольшое раздвоение картинки+черный экран
psy_antenna.ppe – картинка окружающего мира становится бело-желтой как на радаре в конце локации – в зоне действия антенны.
yantar_underground_psi.ppe - эффект сильного раздвоение картинки
blink.ppe - вспышка белого света на секунду-другую
deadcity_wake.ppe - интересный эффект в игре не задействованный – сначала черный экран, раздвоение картинки, красный экран.
vibros.ppe - с этим все понятно – эффект выброса – красное небо, вспышки и т.д.
vibros_p.ppe - еще один выброс, не такой яркий. что-то вроде предвариловки выброса: эффект легкого землетрясения, плавающая резкость, плавающая яркость, звук радиационного фона, плюс ГГ роняет на землю в самом начале. Длительность секунд 20.
radiation.ppe - создает картинку радиационной зоны
fire_hit.ppe - постэффект на попадание в ГГ: заливка экрана красным
Анимации камеры Что из себя представляют анимации камеры – думаю, понятно всем. Вот краткий обзор того, что я успел опробовать.
drunk.anm - анимация алкогольного опьянения. В сочетании с постэффектом alcohol.ppe дает весь набор состояний чувака с бодуна: предметы двоятся, резкость восприятия мира весьма снижена, в глазах белая пелена, качает из стороны в сторону. Одним словом – наглядный пример – пить вредно.
fatigue.anm - анимация усталости, ГГ медленно и слегка пошатывает в разные стороны.
head_shot.anm - анимка попадания пули в голову, ГГ дернет в разные стороны
fusker.anm- что то типа эффекта от удара: ГГ также не хило дернет по сторонам.
punch_effector.anm - по моему то же самое.
prison_1.anm - эффект удара по ГГ с сильным наклоном тела вниз.
dream.anm - любопытная анимка: дает эффект резкого падения ГГ на землю, взгляд камеры будет снизу вверх, потом ГГ поднимается.
shell_shock.anm - эффект легкой встряски камеры
hit_front.anm - что-то типа анимки попадания пули в тело спереди: ГГ слегка шатнет стороны. Также там есть hit_back.anm, hit_front_left.anm, hit_front_right.anm, hit_left.anm – и т.д. Не проверял, но эффект по-моему предсказуемый.
earthquake.anm - дрожь земли, то бишь землетрясение. Обычно идет в паре с выбросом.
dis_consciousness.anm - еще одна прикольная анимка: вращение камеры как будто бы ГГ катится по земле. Используется, насколько я помню, для эффекта переворачивания машины после попадания в аномалию.
Часть вторая. Как бы это использовать?
Для того, чтобы запустить тот или иной эффект из этой папки понадобится некоторое знание скриптов. Эффекты запускаются с помощью операторов level.add_cam_effector ("camera_effects\имя файла.anm", num, loop, "") - для эффектов камеры или анимаций
level. add _pp_effector ("имя файла.ppe", num, loop) - для постэффектов num – любое четырехзначное число loop - логическое значение: ставим либо true, либо false. Если true, то эффект будет проигрываться бесконечно, если false, то один раз.
Выглядеть это будет так. Для примера возьмем выброс и все что с ним связано. Запуск постэффекта выброса:
Как вы заметили, во втором случае, так как анимации в основном лежат в подпапке camera_effects нужно ее указывать. Если же анимка берется из общей папки anims, тогда нет.
Цифры после запятой это sid эффекта который мы ему задаем. Могут быть любыми – четырехзначными. Для чего – поясню ниже. Для остановки эффекта используется два следующих оператора: Для остановки эффекта камеры:
level.remove_cam_effector(num)
для остановки постэфекта:
level.remove_pp_effector(num)
Остановка двух наших предыдущих эффектов будет выглядеть так. останавливаем выброс
level.remove_pp_effector(1974)
Останавливаем землетрясение
level.remove_cam_effector(1975)
Как вы могли заметить, в скобках вместо имен эффектов мы просто написали цифры – sid’ы эффектов, которые мы задали в запуске процессов. Так удобнее, нежели снова вписывать полное имя.
Простейший скрипт запускающий эффект, скажем выброса, будет выглядеть так:
function run_vibros() level.add_pp_effector("vibros.ppe", 1974, false) level.add_cam_effector("camera_effects\earthquake.anm", 1975, false, "") set_postprocess ("scripts\earthshake.ltx") end
Но это будет, во-первых безопасный выброс, во-вторых весьма кратковременный, а в третьих – беззвучный. Для того, чтобы это исправить, добавляем в формулу вот это: local h = hit () h.power = 1 --сила хита, регулируется по желанию h.direction = vector():set( 1, 0, 0 ) --вектор получения хита h.impulse = 1 --импульс h.draftsman = db.actor --от кого дать хит h.type = --тип хита db.actor:hit (h) --кому дать хит
Тип хита можно задать самый разный. Можно хитовать эктора только радиацией hit.radiation или телепатией hit.telepatic или шоком hit.shock
Здесь эктор будет получать хит радиацией, шоком и телепатией. Идем дальше. Звук. После того как расписали, от чего эктору будет хреново и насколько хреново, добавляем в скрипт эти строчки: local snd_obj = xr_sound.get_safe_sound_object([[ambient\earthquake]])
--Все - звук выброса у нас есть. --можно добавить еще звук счетчика Гейгера: xr_sound.set_actor_sound("level_border_detector") xr_sound.set_actor_sound_factor(0.1)
Вот еще набор пугающих звуков из папки sounds: local snd_obj = xr_sound.get_safe_sound_object([[ambient\rnd_outdoor\rnd_dark4]]) local snd_obj = xr_sound.get_safe_sound_object([[ambient\rnd_outdoor\rnd_moan1]]) local snd_obj = xr_sound.get_safe_sound_object([[ambient\rnd_outdoor\rnd_moan2]]) local snd_obj = xr_sound.get_safe_sound_object([[ambient\rnd_outdoor\rnd_dark6]])
local snd_obj=xr_sound.get_safe_sound_object([[ambient\random\rnd_the_horror]])
--Погода из папки weathers level.set_weather("stancia") \ погода на аэс
– или это - судный день или день гнева level.set_weather_fx ("surge_day")
--можно всунуть эффект телепорта (белый экран) level.add_pp_effector ("teleport.ppe", 2009, false)
--или вместо этого --эктора шатает как пьяного level.add_cam_effector("camera_effects\drunk.anm", 1978, false, "")
--и у него «белочки» в глазах level.add_pp_effector("alcohol.ppe", 1976, false) end
В общем, в одну функцию можно "зарядить" сколько угодно эффектов, главное не переборщить, так как некоторые похожие анимки или ppe могут наложиться друг на друга и получится какая-нить хрень. Так что с этим надо быть аккуратным. Это первое. А второе то, что так никто не делает. В том смысле, что в одну кучу все не валит. Тот же выброс по нормальному делается многофазовым. И вот тут в каждой фазе прописывается тот или иной набор эффектов – анимация, постэффект, звук. И для каждой фазы отводится своя длительность. По таймеру. Также расписывается поуровнево какой какой тип хита и какой силы будет получать эктор в зависимотси от локации. А дальше идет остановка всех эффектов соответствующими операторами. Но так как в этой формуле этого нет – весь эффект продлится только несколько секунд, ровно столько сколько длится каждая анимация. Некторые ppe требуют принудительной остановки. Для того, чтобы сделать выброс так как это сделано в глобальных модах, требуется очень хорошее знание скриптов. Сам я написать такой скрипт не смогу, поэтому пользуюсь готовым – из аддона Blowout к ОГСМ. Там вполне рабочий и симпатичный выброс.
А вы можете поэкспериментировать в описанными эффектами для собственного удовольствия. Или замутить с их использованием какую-нить прикольную скриптовую сценку. Позднее я постараюсь дополнить вышеизложенное. Материал на данный момент находится на стадии работы и будет дополняться и исправляться. А пока все. Если если ошибки - комментируйте - исправлю.
Добавлено (16.08.2011, 04:14:13) --------------------------------------------- Типа справочка по С# [spoiler]
Код: Пока только обзор методов работы с файлами и строками. Используемые пространства имен using System; using System.IO;
обзор методов класса FileStream поддерживается произвольный доступ
--получить доступ к файлу FileStream varname = new FileStream(@"D:\test\test.txt", режим открытия,[режим доступа - необязательно]);
--режимы открытия файла FileMode.Open --открыть существующий файл FileMode.CreateNew --создать новый файл. если такой уже есть то возникнет исключение FileMode.Create --создать новый файл. если такой уже есть то будет стерт FileMode.Append --открыть файл если он существует и установить указатель в конец файла. Если файла нет то создать новый.(несовместим с режимами FileAccess.ReadWrite и FileAccess.Read) FileMode.OpenOrCreate --открыть файл если существует. Если нет то создать. FileMode.Truncate --открыть существующий файл и обрезать до нулевой длины. (Типа стереть все.)
--режимы доступа к файлу --аргументы FileStream FileAccess.ReadWrite -- открыть для чтения и записи FileAccess.Read -- открыть для чтения FileAccess.Write --открыть для записи
ПОБАЙТОВОЕ ЧТЕНИЕ --установка позиции varname.Seek(насколько сдвигать в байтах, точка отсчета) --точка отсчета SeekOrigin.Begin --установить точку отсчета в начало файла SeekOrigin.Current --установить точку отсчета в текущую SeekOrigin.End --установить точку отсчета в конец файла
long l = varname.Seek(0, SeekOrigin.End)-- получить длину файла в байтах
varname.Write(откуда, с какой позиции, сколько байтов) -- записать последовательность(массив) байтов в текущий поток varname.WriteByte(num) -- записывает один байт. (перемещает указатель в потоке на кол-во записанных байтов) varname.Read(куда, с какой позиции, сколько байтов) -- считать последовательность(массив) байтов из потока varname.ReadByte() --считывает один байт. (переместить указатель в потоке на кол-во считанных байтов) varname.Position --получить текущую позицию в потоке varname.Lenght -- получить длину потока? Пока не знаю как правильно использовать. varname.SetLenght --установить длину потока
--общие методы varname.Flush()--записать данные из буфера varname.Close(); --закрыть файл
СИМВОЛЬНОЕ ЧТЕНИЕ классы StreamWriter и StreamReader произвольный доступ не поддерживается varname = new StreamWriter(filename) --открыть файл на запись. Если нет то создать varname = new StreamWriter(filename, true\false) -- для чего bool че то забыл varname = new StreamReader(filename) --открыть файл на чтение
varname.ReadLine(s) -- cчитать строку из текущего потока и возвратить ее как string. возвращает null если достигнут конец файла при чтении varname.ReadBlock(куда, с какого символа, сколько символов)-- считать указанное кол-во символов varname.ReadToEnd() --считать все символы до конца потока и возвратить их как одну string varname.Peek() --возвратить следующий символ varname.NewLine --
varname.WriteLine(s) --записать строчку varname.Write(s) --записать фрагмент текста
--СТРОКОВЫЕ МЕТОДЫ string strRep = str.Replace("подстрока", "шаблон"); --заменяет все вхождения указанной подстроки в строке на указанный шаблон char[] a = str.ToCharArray() --преобразует строку в массив символов string strUp = str.ToLower(); --возвращает эквивалент строки в нижнем регистре string strLow = str.ToUpper(); --возвращает эквивалент строки в верхнем регистре string strCop = string.Copy(str) --возвращает копию строки string[] mass = str.Split(); --разделяет строку на элементы результат помещается в массив. Как ставить разделители? string str = string.Join("^",mass); --читает массив строк и соединяет их в одну через любой разделитель. Результат помещается в указанный массив string sub = str.Substring(index, len) --возвращает подстроку строки начиная с указанного стартового индекса длиной в len string newstr = str.PadLeft(str.Length+10) --сдвигает строку вправо вставляя указанное число пробелов перед строкой. В общем методы для выравнивания. string newstr = str.PadRight(str.Length+10) -- должно сдвигать строку влево вставляя указанное число пробелов после строки.
string newstr = oldstr.Remove(0, 5); --удаляет подстроку в указанном диапазоне string newstr = oldstr.Insert(0, "новая подстрока"); --вставляет в указанную позицию( индекс) строки новую подстроку int v = str.IndexOf("я"); --возвращает индекс первого вхождения подстроки в вызывающую строку int m = str.LastIndexOf("я"); возвращает индекс последнего вхождения подстроки в вызывающую строку
int g = str3.IndexOfAny(new char [] {'я','а','б','в'}, 0, str3.Length); --возвращает индекс первого вхождения в строку одного из символов указанных в массиве. Аргументы - массив, стартовый индекс, число позиций для проверки.
string newstr = str.TrimStart(new char[]{'м'}); --удаляет начальные вхождения указанных в массиве символов или символа перед строкой string newstr = str.TrimEnd(new char[]{'м'}); --удаляет начальные вхождения указанных в массиве символов или символа в конце строки оба метода могут вызываться без аргументов. Только зачем?
int res = str1.CompareTo(str2); -- Возвращает отрицательное значение, если вызывающая строка меньше строки str2,положительное значение, если вызывающая строка больше строки str2, и нуль, если сравниваемые строки равны.
int res = string.CompareOrdinal(str,str1); -- аналог CompareTo. Принимает два либо пять аргументов - работает быстрее первой. другой вариант int res = string.CompareOrdinal(строка первая, стартовый индекс,строка вторая,стартовый индекс, максимальное число сравниваемых символов); --
string str = string.Concat(str, str2); --соединяет произвольное число строк в одну str.StartsWith("начало строки") --возвращает true если начало вызывающей строки совпадает с указанным шаблоном str.EndsWith("конец строки") --возвращает true если конец вызывающей строки совпадает с указанным шаблоном пример string str = "начало строки конец строки" if str.StartsWith("начало строки") & str.EndsWith("конец строки")) { Console.WriteLine("совпадает"); } else Console.WriteLine("не совпадает");
МЕТОДЫ ДЛЯ ИЗМЕНЕНИЯ ОБЪЕКТОВ ТИПА STRING
используется специальный класс StringBuilder позволяющий динамически изменять строки так как вообще то в С# поддерживается принцип постоянства строк.
также используется namespace System.Text
System.Text.StringBuilder str = new System.Text.StringBuilder("я помню чудное мгновенье", 0, 10, 100); аргументы - строка, стартовый индекс строки, длина подстроки, размер буфера. Без числовых аргументов по умолчанию вся строка, размер буфера 16 байт. str.Append("передо мной явилась ты"); --добавляет в конец строки(либо согласно указанным параметрам в нужную позицию) новую часть. str.Remove(индекс, кол-во символов); --удаляет подстроку согласно указанным аргументам - стартовый индекс, кол-во символов str.Replace("мгновенье", "секунду"); --замена всех вхождений указанной подстроки\символа на шаблон str.ToString() --преобразование числового значения в строковое
Пока неисследованный класс класс System.Convert //конвертировать массив 8-битовых чисел в строку string s = Convert.ToBase64String(new byte[] { 0, 100 }); //конвертировать двоичные данные из строки обратно в массив чисел Convert.FromBase64String(s);
Добавлено (16.08.2011, 04:15:07) --------------------------------------------- [spoiler]Создание новых переходов между уровнями через скрипт.
Материал для модмейкеров. Переходы между уровнями можно создать и скриптом. Методика существовала уже довольно давно, созданная товарищем sarthur. Но из-за некоторых синтаксических ошибок в главной функции - работала неправильно. Теперь благодаря исправлению скрипта камрадом singapur22 все приведено в порядок.
Так что пользуйтесь.
Что нужно сделать. Первое.
Скопировать нижеприведенную функцию в чистый скриптовый файл.
function create_level_changer( p_story_id, -- STORY_ID нового level_changer (понадобится нам позже) p_position, -- вектор, координаты точки, в которой будет располагаться центр нового level_changer p_lvertex_id, -- level_vertext_id - идентифицируют уровень, на котором будет создан level_changer p_gvertex_id, -- game_vertext_id
p_dest_lv, -- level_vertex_id - идентифицируют уровень, на будет перебрасывать игрока p_dest_gv, -- game_vertex_id p_dest_pos, -- координаты точки, в которой на новом уровне окажется игрок p_dest_dir, -- направрение взгляда игрока p_dest_level, -- название уровня, например "L11_Pripyat" p_silent -- следует задать 1, чтобы подавить вопрос о смене уровня
Все кому я нужен могут найти меня по этому адресу : #EGGO_TM в общем буду рад пообщаться )
StalkerВлад, а что тебе щё нужно напиши может помогу у меня очень много статей на эту тему
Добавлено (19.08.2011, 20:09:13) --------------------------------------------- [spoiler]Формат файлов постпроцессинга (ppe) Вступление Представляю на суд общественности результаты моих изысканий по формату файлов постпроцессинга (.ppe) Нераскрытыми для меня осталось 6 переменных (переменные, которые я для себя назвал free, обязательно идут перед каждой триадой цепочек, управляющих цветом и 3 коэффициента, идут в каждом правиле после байта функции), но простенькие ppe-файлы можно делать и без них. Вообще, оказалось, что возможности ppe были сильно мною переоценены (например тепловизор сделать с их помощью если и можно, то крайне проблематично). Основной недостаток - ppe можно применить только ко всему экрану, на отдельный обьект это не предусмотрено движком (даже стелс-режим кровососа реализуется подменой модели). Так что могу предложить только слегка улучшеный прибор ночного зрения. Получилось так себе, просто убрал шумы, добавил яркости/контрастности, но зато теперь со включенным nv днем не побегаешь - засветка почти как на настоящем (как в кино показывают) и туман гораздо сильнее мешает, клубы тумана почти непрозрачны. По поводу компилятора/декомпилятора, пришел к выводу, что фильтр постпроцесса изделие штучное, смысла в отдельных утилитах нет - я все анализировал, клепал, менял только с помощью блокнота и шестнадцатиричного редактора (кстати, выяснил, что при смене файла постпроцесса необязательно перезапускать сталкера и даже релоад делать ни к чему - изменения подхватываются при следующем выключении постпроцесса в данном случае nv). При анализе ppe сильно помог тот факт, что постпроцесс существует в игре в текстовом и компилированом виде (кому интересно, копайте \gamedata\script\postprocess.script - полный разбор текстовых файлов написан обычным скриптом, там же, в самом конце функция main загружает погоду и файл постпроцессинга, сами файлы лежат \gamedata\config\scripts например proba.ltx). Компилированные ppe лежат в anims.
Практика Итак, ppe-файл - это набор правил, как должен изменяться тот или иной параметр изображения во времени, всего этих параметров - 16 (названия взяты из текстовых версий, чтобы путаницы не было). В отличие от текстовой версии, цепочки должны идти только в этом порядке и их всегда должно быть только 16 (если игра вывалилась с сообщением в логах noise.grain cannot be zero, значит вы что-то пропустили). Выбраная цветовая компонента умножается на коэффициент, то при параметре равным 1 чистый белый цвет останется белым, а чистый черный - черным, остальные преобретут компонентный оттенок. • free1 - об этой переменной чуть ниже • color_add_red • color_add_green • color_add_blue Указанное значение принимается как база для данного цвета, то есть, если у данного пикселя значение цвета меньше указанного, то оно принимается как указанное, а если больше, то остается прежним. Следует заметить, что это единственный набор переменных, задирать значения в которых выше 0.5 не имеет смысла, 0.5 прибавляется где-то в дебрях движка. • free2 • color_base_red • color_base_green • color_base_blue Эта триада правил отвечает за вес цвета в сером/яркостном представлении и в обычном цветном режиме результат не виден. Но при увеличении правила gray становится ясно, что при например color_gray_red = -1 чисто красные компоненты становятся черными, 0 = остаются без изменений, 1 - чисто белыми: • free3 • color_gray_red • color_gray_green • color_gray_blue Переменная управляет величиной обратной насыщенности, то есть, чем больше gray (максимум +1) тем меньше насыщенность изображения: • gray Размытие изображения, при больших значениях (пробовал 25) начинаются косяки, портится нижняя часть, если долго стоять на месте: • blur Раздвоение изображения по горизонтали и вертикали соответственно. Значение указывается относительно экрана, то есть при 1 копии обьекта, первоначально находящегося в центре окажутся точно по краям: • duality_h • duality_v • noise_intensity - интенсивность шума • noise_granularity - размер зерна шума • noise_fps - сколько раз в секунду шум должен пересчитываться Файл всегда начинается с 01 00 00 00 - скорее всего это синатура, по которой движок определяет содержимое(кстати, у файлов anm, которые содержать анимацию камеры и похоже имеют ту же логику строения, что и ppe сигнатурой является 00 11 00 00 ). Затем идет 4 байта неопределенной переменной, которую я для себя назвал free. Всего их 3. Эти переменные стоят только перед триадами, отвечающими за изменения цветов, обычно равны 7C 00 00 00, но есть небольшие вариации. Визуально определить, что меняется при изменении этой переменной я не смог, если кто совершит этот подвиг - вечная слава Затем начинаются цепочки в перечисленном выше порядке. Цепочка начинается с 01 01 ХХ 00, где ХХ - число правил в данной цепочке. Если указано 01 01 00 00 (то есть цепочка с 0 правил, то данная цепочка в постпроцессинге не участвует и сразу после нее должна начинаться следующая цепочка). Основная ошибка, которую можно допустить - несоответствие реального числа правил с указаным в заголовке цепочки. Контроль, контроль, и еще раз контроль. Каждая цепочка состоит из правил изменения переменной. Исходя из скрипта разбора текстовых версий можно предположить, что правил в цепочке не должно быть больше 16 (хотя возможно это только для текстовых).Совершенно необязательно, что этих правил в разных цепочках будет одинаковое количество или даже длиться они будут одинаковое время. Правило содержит в себе всегда ровно 23 байта. Примерное описание - 9A 99 99 3E 00 00 A0 40 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80 ----------- ----------- -- ----- ----- ----- ----------------------- значение время ф k1 k2 k3 • значение - 4-байтная float переменная, обычно меняется от -1 до +1 • время - в секундах 4-байтная float переменная • ф - функция, во всех ppe равна 00, но попробовал менять(не должна быть 04 - игра вылетает). 00,03 - синус, 01,05 - косинус, 02 - линейная, остальные значения игнорируются и правило перестает работать вообще. • k1, k2, k3 - коэффициенты для функций вот с ними почти не разбирался, на линейную фунуцию изменений не выявил, на синус/косинус влияют очень сильно, но на поиск закономерности не хватило времени, по умолчанию равны 00 80 00 80 00 80 • остаток строки во всех файлах 00 80 00 80 00 80 00 80 - изменение визуально ничего не меняет, возможно зарезервировано для следующих версий. Вот и все. Поле для работы как видите еще есть - коэффициенты k[1,2,3] и переменные free[1,2,3] - предлагаю общественности устроить мозговой штурм на них. Напоследок мой файл ночного зрения с комментариями (для использования комменты удалить, шестнадцатиричный код загнать в WinHex и сохранить как anims\x.ppe(имя любое), и не забыть поправить config\misc\postprocess.ltx в нужной секции). 01 00 00 00 // сигнатура
// Не понимаю как, но эта триада подкрашивает уже переведенное в // чб изображение, то есть делаем слегка синеватым результат, заодно // огрубляем слегка красные или зеленые.
6C 10 00 00 // free2 01 01 02 00 // color_base_red CD CC CC 3D 00 00 00 00 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80 CD CC CC 3D 00 00 00 41 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80 01 01 02 00 // color_base_green CD CC CC 3D 00 00 00 00 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80 CD CC CC 3D 00 00 00 41 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80 01 01 02 00 // color_base_blue CD CC 4C 3E 00 00 00 00 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80 CD CC 4C 3E 00 00 00 41 00 00 80 00 80 00 80 00 80 00 80 00 80 00 80
// В сером режиме все красные обьекты - белым, синие и зеленые - черным
Добавлено (19.08.2011, 21:52:14) --------------------------------------------- Сложный квест на оборону лагеря [spoiler]Ceйчac мы coздaдим cлoжный квecт нa oбopoнy лaгepя! Чтo нaм пoтpeбyeтcя? system.ltx character_desc_escape.xml spawn_sections.ltx npc_profile.xml Содержание
• 1 Coздaниe и пoдгoтoвкa фaйлoв o 1.1 Инфoпopшны o 1.2 Диaлoги o 1.3 Инфo для квecтa o 1.4 Пpoфили o 1.5 Фaйл для cкpиптa • 2 Зaпoлнeниe фaйлoв o 2.1 Пpoфили o 2.2 Дoбaвлeниe в ПДA o 2.3 Инфoпopшны
Coздaниe и пoдгoтoвкa фaйлoв Инфoпopшны Зapeгиcтpиpyeм пycтoй фaйл для info_portions. Oткpывaeм фaйл system.ltx, нaxoдим вoт этo - [info_portions]. Hyжнo дoпиcaть в кoнeц cтpoки files этoгo paздeлa, чepeз зaпятyю info_new. B config/gameplay coздaeм фaйл c нaзвaниeм info_new.xml, в нeм пишeм этo: <?xml version="1.0" encoding="windows-1251" ?>
<game_information_portions>
</game_information_portions> Диaлoги Зapeгиcтpиpyeм пycтoй фaйл для диaлoгoв B system.ltx нaxoдим paздeл [dialogs] и пpoдeлывaeм тo, чтo дeлaли c info_portions - дoпиcывaeм в кoнцe cтpoки files - dialogs_new. Coздaдим этoт фaйл в config/gameplay Haзвaниe: dialogs_new.xml, a в нeм тaкaя cxeмa: <?xml version="1.0" encoding="windows-1251" ?> <game_dialogs>
</game_dialogs> Инфo для квecтa Зapeгиcтpиpyeм XML фaйл для квecтa: Haxoдим в config/gameplay фaйл c нaзвaниeм game_tasks.xml, a в нeм пocлe #include "gameplay\game_tasks_by_vendor.xml" вcтaвляeм #include "gameplay\tasks_new.xml" Coздaeм фaйл в пaпкe gameplay c нaзвaниeм tasks_new.xml. B нeгo пoкa пиcaть ничeгo нe нaдo. Пpoфили Зapeгиcтpиpyeм фaйл c пpoфилями. Дoпишитe в paздeл specific_characters_files (тaм гдe [profiles]) - new_profils_chareng Coздaдим в пaпкe gameplay фaйл c нaзвaниeм new_profils_chareng.xml B нeм пишeм: <?xml version='1.0' encoding="windows-1251"?>
<xml>
</xml> Фaйл для cкpиптa Coздaeм в пaпкe gamedata/scripts фaйл c нaзвaниeм new_dialog.script. Зaпoлнeниe фaйлoв Пpoфили Coздaдим пpoфили нaпaдaющиx нa лaгepь NPC. Oткpывaeм фaйл: new_profils_chareng.xml и в нeм пишeм мeждy <xml> и </xml>:
<info_portion id="esc_bandit_ataka_1_dead"></info_portion> <info_portion id="esc_bandit_ataka_2_dead"></info_portion> <info_portion id="esc_bandit_ataka_3_dead"></info_portion> <info_portion id="esc_bandit_ataka_4_dead"></info_portion> <info_portion id="esc_bandit_ataka_5_dead"></info_portion> <info_portion id="esc_bandit_ataka_6_dead"></info_portion> Oткpывaeм пaпкy gamedata/scripts, нaxoдим фaйл c нaзвaниeм new_dialog.script, a в нeм пишeм:
function esc_bandit_ataka_1() alife():create("esc_bandit_ataka_1",vector():set(-141.80,-14.23,-123.79),107755,70) end function esc_bandit_ataka_2() alife():create("esc_bandit_ataka_2",vector():set(-141.80,-14.23,-123.79),107755,70) end function esc_bandit_ataka_3() alife():create("esc_bandit_ataka_3",vector():set(-141.80,-14.23,-123.79),107755,70) end function esc_bandit_ataka_4() alife():create("esc_bandit_ataka_4",vector():set(-141.80,-14.23,-123.79),107755,70) end function esc_bandit_ataka_5() alife():create("esc_bandit_ataka_5",vector():set(-141.80,-14.23,-123.79),107755,70) end function esc_bandit_ataka_6() alife():create("esc_bandit_ataka_6",vector():set(-141.80,-14.23,-123.79),107755,70) end
function bandit_atack_precon(task, info) if (db.actor:has_info("esc_bandit_ataka_1_dead") and db.actor:has_info("esc_bandit_ataka_2_dead") and db.actor:has_info("esc_bandit_ataka_3_dead") and db.actor:has_info("esc_bandit_ataka_4_dead") and db.actor:has_info("esc_bandit_ataka_5_dead") and db.actor:has_info("esc_bandit_ataka_6_dead")) then return db.actor:give_info_portion("ataka_done") end return false end
function ataka_reward(first_speaker, second_speaker) dialogs.relocate_money(second_speaker, 10000, "in") end
Bce, пoздpaвляю, вы нaпиcaли нoвый квecт! Oн бyдeт y Boлкa. Жeлaтeльнo eгo выпoлнять пocлe квecтa c флeшкoй Шycтpoгo!
В этой статье вы узнаете как создать спавнящийся физический объект. Выбираем нужную модель из папки gamedata\meshes. Создаем в файле misc\items.ltx (или своем конфиге) новую секцию, которая будет выглядеть так:
[vedro]:identity_immunities ;vedro — название спавн-секции предмета (Потом мы будем через него спавнить). Пишется латиницей. $spawn = "dynamic_objects\el_tehnika\komp_klava" ;путь спавн-секции в дереве спавн-объектов в СДК (если не пользуетесь СДК, то особой важности не имеет). visual = objects\dynamics\decor\vedro_01.ogf ;Путь до модели физического объекта из папки meshes. cform = skeleton ; Движковые классы (донт тач) class = P_SKELET remove_time = 4 ;Время через которое объект исчезнет. Число до от 0 до 9999, если поставить 9999 то он просуществует очень долго
Второй вариант [vedro]:identity_immunities ;vedro — название спавн-секции предмета (Потом мы будем через него спавнить). Пишется латиницей. $spawn = "physics\object" ;путь спавн-секции в дереве спавн-объектов в СДК (если не пользуетесь СДК, то особой важности не имеет). visual = objects\dynamics\decor\vedro_01.ogf ;Путь до модели физического объекта из папки meshes. class = O_PHYS_S remove_time = 60000 ;Время через которое объект исчезнет. Число до от 0 до 9999, если поставить 9999 то он просуществует очень долго.
Так вроде лучше )
Сохраняем файл. Далее уже стандартно — создаем в скриптовом файле функцию типа function spawn () alife():create("vedro",vector():set(координаты X,Y,Z),level_vertex_id,game_vertex_id) end И вызываем эту функцию через диалог или другой скрипт. [spoiler]
Все кому я нужен могут найти меня по этому адресу : #EGGO_TM в общем буду рад пообщаться )
В последнее время люди постоянно меня спрашивали как создать скрипт гулага для смартеррейнов. Чтобы постоянно не отвечать на каждое отдельное письмо, я решил написать небольшой тутор. Прежде чем начать, хотелось бы предупредить, что не являюсь экспертом в подобного рода вещах и некоторые моменты мне всё еще не ясны до конца, так что вероятно я не смогу объяснить всё правильно. Также я буду использовать ST как абревиатуру смартеррейна. Этот тутор посвящен НПС в ST, но он также сгодится и для мутантов. Предполагаю что вы знакомы с:
- луа программированием - редактированием all.spawn - работа с вейпоинтами(патрульными путями) - утилитами аи
В первую очередить подумайте о своём ST, где бы его заспавнить, сколько нпс/мутантов будут в нем выполнять определенную "работу", когда ST должен активироваться/отключаться, сколько и какие состояния будет у вашего ST и тд. Думаю, наиболее эффективный способ объяснить вам как это работает на примере. Давайте предположим.. мы хотим поставить ST(smart terrain) на Кордоне для 3х бандитов.
1. Спавн ST:
[2515] ; cse_abstract properties (основные параметры) section_name = smart_terrain name = esc_bandits_smart_terrain position = 131.02030944824,0.065616846084595,-248.9094543457 direction = 0,0,0
type = esc_bandits_smart_terrain cond = {-infoportion} capacity = 3 squad = 1 groups = 5 respawn = esc_respawn_inventory_box_0002</source>
type название вашего нового ST(обязательно) cond описывает условия, которые необходимы для включения гулага(по желанию) capacity количество мутантов/нпс, которое может вместить смарттеррейн(обязательно) squad, groups - номер сквада и количество групп(по желанию) respawn название тайника(синяя коробка) куда будут спавниться предметы, когда мы вызовем респавн в ST.(по желанию)
2. Спавн нпс/мутантов и назначение(биндинг) их к нашему ST: для этого мы должны добавить каждому мутанту/нпс определенную логику:
custom_data = <<END [smart_terrains] esc_bandits_smart_terrain = true END
Если таковых нпс не обнаружится то гулаг выберет себе население из числа заспавнившихся в Зоне нпс с подходящими параметрами. Даже если они на другой локации.
3. Добавляем "работу" (логика) для каждого нпс/мутанта из нашего ST(для каждого состояния). Предположии у ST их два: состояние 0 (описывает какие нпс/мутанты "работают" днем) и состояние 1 (ночью). У нас 3 бандита, определяемся:
- bandit1: walker (состояние 0) и kamp (состояние 1) - bandit2: guard (состояние 0) и sleeper (состояние 1) - bandit3: walker (состояние 0 и 1 <= он делает тоже и днём, и ночью)
У нас есть 3 способа добавить логику (работу) для каждого нпс/мутанта, мы будем использовать наиболее общепринятый способ, добавим логику в фаил config\misc\gulag_escape.ltx. Она должна выглядеть примерно так:
;-- bandit1 (walker(прогуливающийся) -> состояние 0, днем) [logic@esc_bandits_smart_terrain_bandit1_walker] active = walker@esc_bandits_smart_terrain_bandit1
4. Теперь нам нужно заскриптовать наш ST. Так что добавим наш код в фаил скрипта \gulag_escape.script. Есть еще несколько моментов, которые мы должны здесь доделать (каждый из этих шагов обязателен):
- грузим логику (работу) для каждого нпс/мутанта и для каждого состояния -> function load_job(...)
if type == "esc_bandits_smart_terrain" then t = {} ;-- "соеденительная секция" для логики, определяем ltx фаилом t.section = "logic@esc_bandits_smart_terrain_bandit1_walker" ;-- no idea, probably describes after what time ;-- npc will use this job again (?) t.idle = 0 ;-- no idea but i guess it's optional t.timeout = 0 ;-- пріоритет t.prior = 100 ;-- нпс будет использовать эту логику, ;-- если ST переключится в это состояние ;-- в этом случае - состояние 0 (день) t.state = {0} ;-- Какой squad и group назначится персонажу принявшему эту работу. t.squad = squad t.group = groups[1] ;-- no idea what means position_threshold t.position_threshold = 100 ;-- описывает нпс в этом состоянии: онлайн или офлайн ;-- онлайн = истина по дефолту t.online = true ;-- описывает рестрикторы (куда нпс могут/не могут пойти) t.in_rest = "" t.out_rest = "" ;-- ввиду особого способа присвоения работ в ;-- smart_terrain.script вы никогда не знаете, какая работа ;-- будет использоваться каждым нпсом; если вы хотите быть уверенным ;-- что конкретный нпс взял конкретную работу, тогда ;-- вам нужно заюзать предикатную функцию; в этом слуае ;-- мы хотим чтобы эта работа присвоилась мастеру бандиту t.predicate = function(obj_info) return obj_info.rank >= 900 end table.insert(sj, t)
;-- bandit2 -> состояние 0 (день) t = {section = "logic@esc_bandits_smart_terrain_bandit2_walker", idle = 0, prior = 5, state = {0}, squad = squad, group = groups[1], in_rest = "", out_rest = ""} table.insert(sj, t)
;-- bandit2 -> состояние 1 (ночь) t = {section = "logic@esc_bandits_smart_terrain_bandit2_sleeper", idle = 0, prior = 5, state = {1}, squad = squad, group = groups[1], in_rest = "", out_rest = ""} table.insert(sj, t)
;-- bandit3 -> состояние 0 (день) и состояние 1 (ночь) t = {section = "logic@esc_bandits_smart_terrain_bandit3_walker", idle = 0, prior = 5, state = {0, 1}, squad = squad, group = groups[1], in_rest = "", out_rest = ""} table.insert(sj, t) end
Еще один момент о состояниях ST, всё зависит от того сколько у вас их в ST. Еще одна важной вещью является добавление логики для каждого состояния. Например в вашем ST такие состояния:
0 - нпс оффлайн 1 - нпс онлайн (день) 2 - нпс онлайн (ночь) 3 - нпс онлайн, они решили напасть на другой ST 4 - нпс онлайн, актор напал на них
К сведению, меня не прет заполнять такое большое количество таблиц, так что обычно я использую эту функцию:
function fill_tbl(section, idle, prior, states, squad, group, in_rest, out_rest, online, gulag_name) local tbl = {}
- автоматически изменяем работу для каждого нпс/мутанта -> function load_states(...)
if type == "esc_bandits_smart_terrain" then return function(gulag) if not db.actor then return gulag.state end if level.get_time_hours() >= 5 and level.get_time_hours() <= 22 then return 0 -- переключает всех мутантов/нпс на дневную работу else return 1 -- ереключает всех мутантов/нпс на ночную работу end end end
- убедитесь что наш ST будет использоваться только для бандитов -> function checkStalker(...)
if gulag_type == "esc_bandits_smart_terrain" then return npc_community == "bandit" end
Так же существуют универсальные гулаги General_lager для сталкеров. Они считаются упрощенными гулагами.
Пример, создаем смарт:
[9999] ; cse_abstract properties (основные параметры) section_name = smart_terrain name = esc_gen_lager position = 131.02030944824,0.065616846084595,-248.9094543457 direction = 0,0,0
General_lager автоматически соберет все точки путей на уровне начинающиеся на имя смарта(в нашем случае esc_gen_lager) и разделит их названия таким образом: (имя_смарта)_(аи схема)_(номер если требуется,напрмер если 2 walkerа то 1 и 2 соответственно)_(поднастройка схемы)_(состояние гулага 1 или 0,день или ночь) пример: esc_gen_lager _walker _1 _walk _1 (walk то же что и path_walk,look это path_look соответственно)
Пришедший на general_lager сталкер рандомно примет любую свободную работу работой считается комбинация схем с одним номером. например esc_gen_lager_walker_1_walk_1 и esc_gen_lager_walker_1_look_1 это схема дневной работы одного персонажа из гулага.
Так что для каждого general_lager нужны заранее расставленые точки путей для него.
Добавлено (19.10.2011, 14:33:09) --------------------------------------------- вот ещё одна про респавн
RESPAWN NPC----------------------------------------------------------------------------- Для создания респавна, понадобится два файла, all.spawn и spawn_section.ltx. Это минимальный набор. Для полной настройки респавнера, нужны ещё character_desc_simulation.xml и npc_profile.xml.
; se_respawn propertiesЕсли используется программа XrSpawner, то копируем любую секцию respawn; меняем имя объекта, координаты, и в окошке скрипт прописываем
Где respawn_section = id респавн-секций из файла spawn_section.ltx. Цифры после запятой, означают отношение одной секции к другой. Если планируется одинаковое колличество респавна из каждой секции, то цифры ставить не требуется. max_count = колличество респавн-запросов за период времени, пока ГГ находится на уровне. При переходе на другой уровень, счётчик max_count сбрасывается. (ставить не обязательно) min_count = минимальное колличество нпс, для экстренного респавна. (ставить не обязательно) max_spawn = колличество нпс за один цикл респавна. idle_spawn = тип спавна. Известно три типа medium, often, seldom. Обычно, используется тип medium. conditions =Условие респавна. Можно установить проверку условий (инфопоршень), где респавн будет происходить только при выполнении условия. Номер означает общий процент выполнения респавна. Например так conditions = {+инфо} 100, 0 где, респавн будет происходить со сто-процентной вероятностью, только при наличии установленного инфопоршна.
-------------------------------------------------------------------------------- RESPAWN MUTANTOV------------------------------------------------------------------------------- В принципе, респавн мутантов идентичен респавну нпс. Но с одним лишь отличием, которое его значительно упрощает. Вся настройка респавна мутантов, ограничивается секцией в файле all.spawn. Вписываем секцию респавнера, как указано в -RESPAWN NPC-. Все настройки идентичны, кроме строки respawn_section = . В ней прописываем типы мутантов, из файла se_respawn.script. Такие как flesh_weak, flesh_normal, dog_weak, boar_strong, и т.д..
-------------------------------------------------------------------------------- ОБЩИЕ НАСТРОЙКИ-------------------------------------------------------------------------------- Обязательные условия респавна
1. На уровне обязательно должен быть хоть один гулаг для респавнящихся нпс и мутантов. Так как колличество неписей для респавна, ориентируется по доступным местам в гулагах.
2. Респавн производится, только если под гулагами есть доступные работы.
3. На каждом уровне, респавн ограничен определёнными типами неписей. Данные ограничения установлены в файле miscsmart_terrain_presets.ltx. Чтобы сделать возможным респавн неписей, не предусмотренных на данном уровне. Надо в секции нужного уровня, дописать требуемые типы неписей.
Для проверки созданного респавнера, можно не ждать когда сработает системный респавн, а вызвать принудительно респавн в нужной секции. Для этого, прописываем в любой логике, вызов функции %=respawner_spawn(имя респавнера)%. Где, имя респавнера - имя секции, созданой в файле all.spawn. Скобки обязательны.
Например, можно создать рядом с зоной респавнера, временный рестриктор. И в кастом дате рестриктора, прописать вызов функции принудительного вызова респавна, в нужном респавнере, при входе ГГ в зону рестриктора
-------------------------------------------------------------------------------- RESPAWN ITEMS------------------------------------------------------------------------------- Респавн инвентаря в инвентарных ящиках, несколько отличается от способа респавна неписей.
3. Создаём респавнер в файле all.spawn. Координаты и размеры зоны респавнера, должны быть такими, чтоб инвентарный ящик поместился внутри зоны респавнера
[10001] ; cse_abstract properties section_name = respawn name = имя респавнера position = координаты x,y,z direction = 0,0,0
respawn_section = список вещей респавна. Как и в респавнерах неписей, возможна прописка соотношений между вещами. Например
respawn_section = wpn_pm,2, vodka,2, medkit,1, energy_drink,2, ammo_9x18_fmj,3 idle_spawn = тип спавна. Инвентарь не имеет типов спавна, поэтому ставится (-1).
parent = ай-ди инвентарного ящика, в котором производить респавн вещей.
item_spawn = (truefalse) возможность включения и отключения респавна. Можно задавать условия.
max_count = количество респав-циклов за определённый период времени. (Пока ГГ находится на уровне. После перехода ГГ на другой уровень, счётчик сбрасывается).
4. Создаём вызов респавна. Обычно вызов производится из smart_terrain. В custom_data любого подходящего smart_terrain, под секцией [smart_terrain], вписываем вызов респавна
[smart_terrain] respawn = имя респавнера И респавн будет производиться, при каждом пополнении нпс, под данным смартом.
rd_team, как можно сделать локации с монстрами??? Добавлено (24.08.2011, 11:50:33) --------------------------------------------- rd_team, только новые локации
как создавать берешь сдк и делаешь карту потом все нужные для нее конфиги ну а монстров потом можно через ал спавн заспавнить ну или скриптом а ещё проще через тот же сдк при создании
Все кому я нужен могут найти меня по этому адресу : #EGGO_TM в общем буду рад пообщаться )
* o article id - внутреннее имя статьи, именно на него ссылаются в файлах игры o name - имя статьи, отображаемое в игре, подгружается из строкового массива o texture - картинка и её позиция в статье, в данном случае мы использовали обычный белый шум o text - текст статьи, отображаемый в игре, подгружается из строкового массива
* Добавим в, например, config\gameplay\info_l01escape.xml (инфопорции уровня "Кордон") ссылку на получение статьи - скажем, к трупу у туннеля, при обыске которого выдается информация о аномалиях в туннеле (вы его обнаруживаете, проходя второе спецзадание от Сидоровича). Найдем эти строки и дополним их:
Code
<!-- труп у аномалии --> <info_portion id="esc_tutorial_dead_novice"> <article>tutorial_moving_anomaly</article> '''<article>zone_anomalies_activation_basic</article>''' </info_portion>
Главное - никогда не путайте article id, name и text статьи. Я в данном примере это сделать легко. Лучше называйте их непохожими друг на друга названиями.
* Так как в статье у нас есть такое поле:
Code
<text>enc_zone_anomalies_activation_basic</text>
Да и название тоже не написано прямо, а ссылается на определенную строку, то добавим этот самый text в config\text\rus\string_table_enc_zone.xml, в нашем случае:
Code
<string id="enc_zone_anomalies_activation-basic"> <text>Активация - базис</text> </string> <string id="enc_zone_anomalies_activation_basic"> <text>С артефактами связана, помимо всего прочего, ''(ну, и так далее, там большой текст)''...</text> </string>
Дополнительно
Чтобы статья добавлялась при получении определенного задания, в ..._task.xml (вместо ... стоит название уровня) нужно прописать конструкцию вида:
Code
<article>название_задания_descr</article>
Авторы
Статья создана: BAC9-FLCL
Quote
А можно сделать так, чтобы все статьи в энциклопедии были сразу
В файле gamedata\scripts\bind_stalker.script найnи такой код
Code
-- if not has_alife_info("encyclopedy") then -- self.object:give_info_portion("encyclopedy") -- end
и удалить вот это "--" и в начале игры будут все статьи в энциклопедии.
--------------------------------------------- Спавн и удаление переходов между локациями скриптовым методом
1) Спавн переходов: Создаём в папке \gamedata\scripts скриптовый файл с любым именем, например, y_level.script и вписываем в него следующее:
Code
function create_level_changer( p_story_id, -- STORY_ID нового level_changer (понадобится нам позже) p_position, -- вектор, координаты точки, в которой будет располагаться центр нового level_changer p_lvertex_id, -- level_vertext_id - идентифицируют уровень, на котором будет создан level_changer p_gvertex_id, -- game_vertext_id
p_dest_lv, -- level_vertex_id - идентифицируют уровень, на который level_changer будет перебрасывать игрока p_dest_gv, -- game_vertex_id p_dest_pos, -- координаты точки, в которой на новом уровне окажется игрок p_dest_dir, -- направрение взгляда игрока p_dest_level, -- название уровня, например "L11_Pripyat" p_silent -- следует задать 1, чтобы подавить вопрос о смене уровня (автоматический переход) ) local obj = alife():create("level_changer", p_position, p_lvertex_id, p_gvertex_id)
local packet = net_packet() obj:STATE_Write(packet)
-- свойства cse_alife_object local game_vertex_id = packet:r_u16() local cse_alife_object__unk1_f32 = packet:r_float() local cse_alife_object__unk2_u32 = packet:r_u32() local level_vertex_id = packet:r_u32() local object_flags = packet:r_u32() local custom_data = packet:r_stringZ() local story_id = packet:r_u32() local spawn_story_id = packet:r_u32()
-- свойства cse_shape local shape_count = packet:r_u8() for i=1,shape_count do local shape_type = packet:r_u8() if shape_type == 0 then -- sphere local center = packet:r_vec3() local radius = packet:r_float() else -- box local axis_x_x = packet:r_float() local axis_x_y = packet:r_float() local axis_x_z = packet:r_float() local axis_y_x = packet:r_float() local axis_y_y = packet:r_float() local axis_y_z = packet:r_float() local axis_z_x = packet:r_float() local axis_z_y = packet:r_float() local axis_z_z = packet:r_float() local offset_x = packet:r_float() local offset_y = packet:r_float() local offset_z = packet:r_float() end end
-- свойства cse_alife_space_restrictor local restrictor_type = packet:r_u8()
-- свойства cse_level_changer local dest_game_vertex_id = packet:r_u16() local dest_level_vertex_id = packet:r_u32() local dest_position = packet:r_vec3() local dest_direction = packet:r_vec3() local dest_level_name = packet:r_stringZ() local dest_graph_point = packet:r_stringZ() local silent_mode = packet:r_u8()
packet:w_u16(p_dest_gv) -- destination game_vertex_id packet:w_s32(p_dest_lv) -- destination level_vertex_id packet:w_vec3(p_dest_pos) -- destination position packet:w_vec3(p_dest_dir) -- destination direction (направление взгляда) packet:w_stringZ(p_dest_level) -- destination level name packet:w_stringZ("start_actor_02") -- some string, always const packet:w_u8(p_silent) -- 1 for silent level changing
packet:r_seek(0) obj:STATE_Read(packet, packet:w_tell()) news_manager.send_tip(db.actor, "Новый путь", nil, nil, 20000) end
2) Далее, в самый конец этого файла вписываем собственно функцию спавна перехода, которую потом надо будет вызвать либо из диалога либо из инфопоршня либо по какому-нибудь условию. Например, создадим двухсторонний переход в тайник Стрелка с Агропрома и обратно на Агропром:
Code
function create_agro_taynik() -- создается переход c АГРОПРОМА В ТАЙНИК СТРЕЛКА if (not has_alife_info( "teleport_to_taynik" )) then y_level.create_level_changer(20015, vector():set( -45.089,-0.637,-34.386 ),194126,654, 3053, 717, vector():set( -75.975,-6.726,-74.848 ), vector():set( 0.0,1.5,0.0 ),"l03u_agr_underground",0) db.actor:give_info_portion("teleport_to_taynik") end -- создается переход ИЗ ТАЙНИКА СТРЕЛКА НА АГРОПРОМ if (not has_alife_info( "teleport_from_taynik" )) then y_level.create_level_changer(20016, vector():set( -81.394,-4.946,-70.915 ),2709,717, 194118, 695, vector():set( -47.207,-0.364,-40.833 ), vector():set( 0.0,3.0,0.0 ),"l03_agroprom",0) db.actor:give_info_portion("teleport_from_taynik") end end
--Обратите внимание, что название фашего скриптового файла необходимо ставить в функцию спавна. В представленном варианте - это y_level, у Вас может быть другое имя. --Обратите внимание, что каждомому создаваемому переходу необходимо присвоить свой уникальный story_id и зарегистрировать его в файле: game_story_ids.ltx (путь: \gamedata\config ). В примере заданы для каждого из двух новых переходов стори_айди 20015 и 20016 соответственно. Важно, чтобы создаваемые стори_айди не повторяли уже существующие, иначе будет вылет. Лучше брать значения больше 20000, а лучше внимательно просмотреть существующие номера стори_айди в файле: game_story_ids.ltx 3) Кроме этого, чтобы переход отображался на карте и у него было название, необходимо прописать его в файл: level_tasks.script (путь: \gamedata\scripts) по аналогии с остальными и дать новому переходу название. Вот так:
Code
obj = sim:story_object(20015) if obj then level.map_add_object_spot(obj.id, "level_changer", "В Тайник Стрелка") end obj = sim:story_object(20016) if obj then level.map_add_object_spot(obj.id, "level_changer", "На Агропром") end
Следите, чтобы в конце функции, в которую добавляете новые регистрации переходов (особенно, если добавляете в самый конец функции), обязательно было, как и изначально, три end 4) В функции спавна переходов выдаются инфопоршни. По одному на каждый создаваемый переход. Эти инфопоршни необходимо прописать в вашем файле с инфопоршнями. В примере это два инфопоршня: teleport_to_taynik и teleport_from_taynik 5) Чтобы заспавнить переход только в один конец, то действуем по аналогии, но сама функция спавна будет немного другой. Например, тайная тропа с Ростока на Армейские Склады:
Code
function tropa_rostok() -- создается переход c РОСТОКА НА АРМЕЙСКИЕ СКЛАДЫ if (not has_alife_info( "info_tropa_rostok" )) then y_level.create_level_changer(20017, vector():set( -289.735,0.099,192.842 ),295,1340, 13812, 1847, vector():set( -408.979,-13.798,400.699 ), vector():set( 0.0,2.0,0.0 ),"l07_military",0) db.actor:give_info_portion("info_tropa_rostok") end end
6) В функции спавна переходов можно так же задать направление взгяда актёра, когда он оказывается в точке после перехода. Это задаётся в строке: vector():set( 0.0,2.0,0.0 ),"l07_military",0) Имеет значение только вторая координата. Она задаёт как раз ориентацию на местности. Показатели очень просты: 0.0 (по умолчанию) это смотреть на север. -1.5 - это 45 градусов вправо (на восток). 3.0 - это 90 градусов (смотреть будет строго на юг). 1.5 - это 45 градусов влево (на запад). 7) Если нужно удалить какой-нибудь переход, то в том же файле вписывем функцию, где указываем стори_айди перехода, который нужно удалить. Пример:
Code
function delete_radar_mg() local sim = alife() local se_obj = sim:story_object(20044) if se_obj then sim:release(se_obj, true) end local actor = db.actor end
Всё. Вызываем данную функцию из удобного для Вас места (диалог или инфопоршень) и переход исчезнет.
Одинокий-Волк, реально ли на чистом тч с прикрученным фриплеем сделать респавн не только неписей, но и разных предметов (патроны, аптечки, арты ) через определенный промежуток времени?
Профессиональный сталкер. Знаю о Зон все. Ну или почти все.
Автор: Modoskea Динамическое снаряжение при старте Разбираем на примере создания рандомного оружия при старте. Заходим в папку - gamedata\scripts\ Там находим файл bind_stalker.script Открываем ищем строку (Если кто не знает быстрый поиск Ctrl+F) - function actor_binder:update(delta) Под этой строкой есть строка - object_binder.update(self, delta) Вот под ней пишем:
items_start_random.start() -- весь код будет в скрипте items_start_random.script дабы не гадить в апдейте
Код будет выглядит так:
Код
function actor_binder:update(delta) object_binder.update(self, delta)
items_start_random.start()
Теперь создаем файл items_start_random.script И пишем в него:
Код
function start() if not has_alife_info("start_item_random") then --Проверка на отсутствие инфо поршня local wpn_slot2 = { --Локальная с 5 секциями оружия во 2 слот [1] = "ak74", [2] = "ak74u", [3] = "bm16", [4] = "toz34", [5] = "mp5" }; local wpn_gun = { --Локальная с 5 секциями оружия во 1 слот (пистолет) [1] = "pm", [2] = "pb", [3] = "fort", [4] = "walther", [5] = "hpsa" }; local ra1, ra2 = math.random(1,5), math.random(1,5) alife():create("wpn_"..wpn_gun[ra2], db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) alife():create("wpn_"..wpn_slot2[ra1], db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) -- С оружием все оно у нас появляется рандомно, а как же патроны? вот они в низу V for i = 1,math.random(1,6) do -- Тут у нас цикл патроны появиться тоже примерно радомно от 1 до 6 пачек if ra1 <= 2 then --[[Ищем какое нам выдано оружие по родному из локального массива wpn_slot2 и если ак74 или ак74у то спавним патроны 5.45 со стальными идентично -]] alife():create("ammo_5.45x39_fmj", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) elseif ra1 == 3 or ra1 == 4 then alife():create("ammo_12x70_buck", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) elseif ra1 == 5 then alife():create("ammo_9x19_fmj", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) end if ra2 <= 3 then alife():create("ammo_9x18_fmj", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) else alife():create("ammo_9x19_fmj", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id()) end end db.actor:give_info_portion("start_item_random") --Выдаем инфо поршень end end
Теперь создадим инфо поршень start_item_random думаю тут нечего сложного нет.
весь код будет в скрипте items_start_random.script дабы не гадить в апдейте
А зачем вообще "гадить в апдейте", в итоге, эта фукция будет проверяться всю игру. Т.к. функция нужна всего лишь раз и при начале игры, не лучше ли будет вызывать функцию при старте игры?
Ну не большая нагрузка на движок, ну можно в разные места записать, можно через рестриктор даже, через можно также в скриптах в net_spawn в бинде ну в общем много где можно. А так нагрузка на двиг не большая совсем
Не злоупотребляй чрезмерным цитированием. Устное предупреждение. comador
Сообщение отредактировал comador - Понедельник, 21.10.2013, 23:40:45