День рождение портала, на 1 год! (Глав. стр). S.T.A.L.K.E.R. 2: уникальные концепт-арты. (Глав. стр). A.N.T.I.S.T.A.L.K.E.R. 11 серия. (Глав. стр). Мультиплеер в Метро всё таки будет... (Глав. стр).
CryZone Sector 23 - Закрыт. (Глав. стр).
Сергей Брин: В Google тоже любят S.T.A.L.K.E.R. (Глав. стр).
В скриптах есть одна единственная функция, отвечающая за спавн объектов: alife():create(section,position,levelvertex,gamevertex)
Строго говоря, их две: create create_ammo но различия между ними не существенны. Imp 22:45, 23 июля 2007 (EEST)
Первый параметр - секция в конфигурациях, описывающая объект, например "bolt","med_kit" - это простые секции, простых объектов а есть объекты, которые переходят в онлайн/оффлайн, это неписи, монстры и так далее, например mil_killer_respawn_2 - спавнится снайпер группировки киллеров.
С позицией, думаю объяснять не надо, только существует нюанс - высота это Y, а не Z. Задать позицию можно такой конструкцией vector():set(x,y,z), где x, y и z - координаты точки на уровне, где спавним объект.
Дальше сложнее, так как сам толком сформулировать не могу.
Начнем от простого к сложному. На каждом уровне много объектов, все объекты состоят из полигонов, у каждого полигона есть вершины – вертексы.
Именно они и должны здесь указываться, зачем - не особо понимаю, скорее всего для точного позиционирования объекта. Например, можно получить вертекс ближайший к актору - db.actor:level_vertex()
Дальше идет гораздо более интересный параметр game_vertex, это почти то же самое, что и level_vertex, но (!) это глобальные величины! Если level_vertex считается для уровня, то game_vertex - для всей игры, и нужен он для того, чтобы указать на какой карте спавнить объект (более вразумительного объяснения я не нашел).
Соответственно, чтобы заспавнить что-нибудь на другой карте, достаточно указать game_vertex в четвертом параметре Например: db.actor:game_vertex()
Итак, чтобы, например, заспавнить болт под ногами актора, пишем: alife():create("bolt",db.actor():position(),1,db.actor:game_vertex())
Почему 1, а не level_vertex? Проверено - разницы особой нет, какой level_vertex, хотя в некоторых случаях надо прописывать валидный вертекс, а то предмет может просто заспавнится не там, где планировалось... Но по большей части все проходит нормально и с единицей. (Игнорирование level_vertex может приводить к проваливанию произведенных предметов/персонажей под землю.) А вот game_vertex решает все - он указывает на каком уровне спавнить предмет, поэтому его надо указывать. Теоретически можно просто найти для каждого уровня по одному game_vertex'у и использовать их в скриптах. На самом деле game_vertex показывает какой фрагмент карты используется (вся карта разбита на кусочки имеющие сквозную нумерацию по всем уровням и game_vertex выбирает нужный) соответсвенно неправильное использование черевато....
Кроме того - есть еще один параметр - ID объекта, если указать ID NPC или актора - то предмет заспавнится у него в инвентаре.
Пример (спавним артефакт Медуза в инвентаре у актора): alife():create("af_medusa", db.actor():position(), 1, db.actor:game_vertex(), db.actor:id())
Функция спавна возвращает серверный объект, то есть ни NPC, ни монстра ни что-либо еще.
Серверный обьект позволяет свеже созданного NPC или тайник затарить разными рулезами/артефактами. Например, вот так создадим перед входом к Сидоровичу долговца и засунем в него пачку патронов: local obj local a = vector() -- Задаем тип переменной local dir = db.actor:direction()
a.x = -243.61 -- координата X a.y = -19.52 -- высота Y a.z = -127.17 -- координата Z
obj = alife():create("bar_dolg_respawn_3",a,13193,8,65535) alife():create_ammo("ammo_9x18_fmj", obj.position, obj.m_level_vertex_id, ob j.m_game_vertex_id, obj.id, 20) -- число патронов
Кстати, create_ammo - практически тоже самое, что и create, разница в том, что create_ammo предназначена специально для спавна патронов и позволяет создавать неполные пачки патронов. Возможно есть еще какие-то отличия. Стоит учесть, что сами авторы игры спавнят патроны исключительно через create_ammo. Imp 22:38, 23 июля 2007 (EEST)
Просто минимальный набор - координаты, ID, секция,а из него (серверного объекта) обычно нужен только ID, так как по ID можно получить этот самый серверный объект: (alife():object(id))
Его можно использовать, чтобы поставить метку, например, но я его лично использую для других целей - спавн сложных объектов, конкретно – NPC.
Например надо решить следующую задачу - надо создать наемника, сменить ему группировку и изменить его инвентарь, ну и в нагрузку - сделать другом для игрока.
В определенный момент заспавненый объект переходит онлайн, в этот момент вызывается callback - net_spawn.
Что мы делаем? Сверяем ID онлайн объекта с сохраненным ID!
Если они совпадают, например так: if obj:id()==saved_id then ...
Важно то, что у серверного объекта ID - это параметр, а у онлайнового объекта ID получается с помощью функции. Это важно, а то можно прогореть.
Итак, мы поймали нашего киллера по ID.
Далее все очень просто - вызываем команды для спавна гаусса и патронов к нему в инвентаре NPC (см. выше), меняем группировку специальной функцией, и делаем его другом.
Зачем такие сложности? Просто в оффлайне NPC как бы не существует, есть только косвенное упоминание о нем, и, плюс, все эти функции работают именно с объектом типа "NPC", а не с серверными объектами. Практика (часть 1)
1. Чтобы не повторяться в описании создания нового квеста, просто изучите статью по созданию квестов от Fr3nzy – лучшей статьи на эту тему я просто не видел Мы просто свяжем все воедино и научимся спавнить объекты из скрипта.
Небольшое отступление:
Почему предпочтительнее делать спавн скриптом, а не через тот же xrSpawner? Программа xrSpawner, при всех своих достоинствах, обладает одним недостатком, а именно – она делает спавн через файл all.spawn, что приводит к: Невозможности совместить два мода, такой спавн использующих Необходимости каждый раз начинать новую игру
При спавне через скрипт ситуация иная: в подавляющем большинстве случаев, ранее сохраненные игры будут работать, что не может не радовать
Итак, определимся с квестом.
Задача: после разговора с Сидоровичем спавним зомби на территории фабрики в первой локации. Для того, чтобы не повредить оригинальный сюжет игры, задание будет выдаваться после прохождения квеста с флешкой Шустрого, так как появись там зомби одновременно с бандитами и Шустрым... я думаю, исход боя предрешен
Реализация: Постараюсь описать все действия максимально подробно, буквально по шагам. Первым делом запустите игру
В консоли введите команду: rs_stats on или rs_stats 1
Тем самым мы включаем вывод информации на экран. Далее вводим еще одну команду: demo_record 1
И «летим» на фабрику. Нам нужно выбрать место для спавна объектов и данный режим как нельзя лучше подходит для реализации задуманного. Помещаем камеру в точке предполагаемого спавна и записываем координаты - у меня получились 115, -6, -16.
Для выхода из режима demo_record нажимаем Esc, в консоли пишем rs_stats off или rs_stats 0 (убираем вывод информации).
Другой способ получения тех же сведений - прийти в нужное место и запустить там скрипт, который выдаст все нужные координаты. Я пользуюсь следующим скриптом (вызываю общеизвестным способом, через main_menu): function main_menu:main_cheat_f3() -- Выдадим сообщение о нашем местоположении local text local vid local gvid local a = vector() -- Тип переменной local text
a = db.actor:position() -- Наше положение в координатах vid = db.actor:level_vertex_id() gvid = db.actor:game_vertex_id() text = "Позиция:\\nX= "..a.x.."\\nY= "..a.y.."\\nZ= "..a.z.."\\nlevel_vertex= "..vid.."\\ngame_vertex_id= "..gvid news_manager.send_tip(db.actor, text, nil, nil, 30000) end
В результате не нужно эксперементировать мы сразу получаем все, в том числе и level_vertex и game_vertex. Imp 22:38, 23 июля 2007 (EEST)
Выходим из игры, идем в папку с установленной игрой и создаем каталог gamedata (предполагается, что «лепим» свой «мод» на «чистую» игру, без установленных модов, и имеем распакованные ресурсы игры в папке, скажем, gamedata source).
В папке gamedata создаем папку config, а в ней - папку creatures. Скопируем из оригинальной папки файл m_zombie.ltx и откроем его на редактирование.
В файлах игры присутствуют 5 моделей гражданских зомби: файлы zombi_1.ogf, zombi_1_ghost.ogf, zombi_2.ogf, zombi_trup.ogf, zombi_trup_2.ogf
Вернем в игру их всех
Уже имеются секции: [zombie_weak]:m_zombie_e, [zombie_normal]:m_zombie_e, [zombie_strong]:m_zombie_e и [zombie_immortal]:zombie_strong.
Два последних типа используют одну и ту же модель zombi_trup.ogf, хм... непорядок, исправляем. Последняя секция выглядит теперь так: [zombie_immortal]:zombie_strong $spawn = "monsters\zombies\zombie_immortal" visual = monsters\zombi\zombi_trup_2 panic_threshold = 0.05
Добавим пятую модель.
Для этого в конце файла создадим секцию: [zombie_ghost]:zombie_strong
Это означает, что наш пятый зомби наследует все параметры zombie_strong, мы добавим лишь визуальное представление.
2. Пишем скрипт спавна. В папке gamedata создаем новую папку scripts, в ней создаем новый текстовый документ и называем его esc_zombie.script.
Отступление третье:
При написании статьи использовался оригинальный скрипт zombie_story.script из horror-mod’а. Концепция спавна перенесена практически без изменений, поэтому на авторство этого способа спавна я никоим образом не претендую
Итак, открываем наш пустой файл на редактирование, первой строкой объявляем переменную, в которой хранятся наши зомби: local zombie_types = {"zombie_weak", "zombie_normal", "zombie_strong", "zombie_immortal", "zombie_ghost"}
Далее пишем функцию: function spawn_zombies( position, total ) local zombie_index -- тип зомби из массива zombie_types local new_pos, x_offset, z_offset -- объявляем переменные for zombie_index=1, total do -- крутим цикл столько раз, сколько задает переменная total x_offset = math.random(5) -- случайное (рандомное) x от 1 до 5 z_offset = math.random(5) -- случайное (рандомное) z от 1 до 5 new_pos = position -- передаем координаты в функцию new_pos.x = new_pos.x + x_offset -- прибавляем к указанной нами координате x полученное выше рандомное x new_pos.z = new_pos.z + z_offset -- прибавляем к указанной нами координате z полученное выше рандомное z -- Ниже, собственно и вызывается функция спавна случайного типа зомби zombie_types[math.random(5)] привязанного к нашим координатам alife():create(zombie_types[math.random(5)],new_pos,db.actor:level_vertex_id(),db.actor:game_vertex_id()) end end
И последнее: function zomby_story_1( actor, npc ) -- десять зомби на фабрике (Кордон) local spawn_point = vector():set( 115, -6, -16 ) -- здесь указываем координаты, выбранные нами для спавна, когда «летали» камерой spawn_zombies( spawn_point, 10 ) -- собственно вызов предыдущей функции с передачей ей координат и количества объектов end
Все. Сохраняем и закрываем файл.
Продолжаем разговор
Для того, чтобы игра не вылетала после того, как мы добавили новый тип монстров, их нужно добавить в файл xr_statistic.script. Итак, скопируем этот файл из папки игры scripts в нашу папку к файлу esc_zombie.script и откроем на редактирование.
Добавим в local killCountProps к монстрам строчку: zombie_weak = 1, zombie_normal = 2, zombie_strong = 3
В local sect_alias строчку: zombie_weak = "zombie_weak", zombie_normal = "zombie_normal", zombie_strong = "zombie_strong"
А ниже в monster_classes строчку: [clsid.zombie_s ] = "zombie"
В функцию getNpcType(npc) добавляем конструкцию: elseif npc:character_community() == "zombie" then community = "zombie"
Сохраняем изменения и закрываем файл.
Все будет работать на ура, пока мы не попробуем обыскать убитого зомби. Как только мы это сделаем, игра вылетит с примерно такой ошибкой. Expression : fatal error Function : CInifile::r_string File : D:\xray-svn\xrCore\Xr_ini.cpp Line : 351 Description : <no expression> Arguments : Can't find variable icon in [zombie_weak]
Все верно – игра не знает какую иконку нам показывать для зомби. Иконки монстров хранятся в файле ui_npc_monster.dds. Здесь есть два варианта: Если дружите с Фотошопом, отредактировать этот файл (нарисовать, добавить иконки); Взять готовый из любого мода, естественно, с разрешения авторов мода. Сейчас мы пропустим данный аспект и присвоим нашим зомби иконки контролера
Вернемся к файлу m_zombie.ltx и в секцию [m_zombie_e]:monster_base впишем параметр icon = ui_npc_monster_kontroler
Все. Вылетов не будет.
3. Тема данной статьи не предусматривает подробного описания того, как сделать новый диалог. В начале статьи я упомянул источник, где можно найти исчерпывающую информацию по созданию диалогов, могу также привести в пример статью по созданию диалогов от BAC9-FLCL.
И также связанный с ним файл stable_dialogs_escape.xml. В самом начале файла пишем следующее: <string id="escape_trader_talk_info_7770"> <text>Происшествий никаких не было?</text> </string> <string id="escape_trader_talk_info_7771"> <text>Да знаешь... Вроде как тихо все у нас. Хотя, вот, вспомнил! Говорили мне на днях, что на фабрике, ну, там, где бандюки околачиваются постоянно, видели какиих-то то ли людей, то ли призраков... Мало ли что спьяну почудится - я и сказал этим паникерам, мол, закусывать надо! Хех, блин, алкаши...</text> </string> <string id="escape_trader_talk_info_7772"> <text>Дык мне по любому мимо фабрики топать - заодно и посмотрю на этих "людей-призраков".</text> </string> <string id="escape_trader_talk_info_7773"> <text>Да я как-то не собирался в ту сторону...</text> </string> <string id="escape_trader_talk_info_7779"> <text>Ну, смотри сам, все равно будь осторожен.</text> </string> <string id="escape_trader_talk_info_7777"> <text>Ага. Сходи, проветрись. Потом зайдешь, расскажешь, что там и как.</text> </string> <string id="esc_bridge_soldiers_start_11"> <text>Здесь проход воспрещён, сталкер.</text> </string>
Все. Можно запускать игру, идти на Кордон, после разговороа с Сидоровичем, в зависимости от выбранного Меченным решения, бежим на фабрику и … смотрим сами :)
Однотипные квесты
Описание
Как известно, при разговоре с некоторыми NPC (Сахаров, Бармен, Сидорович, Лукаш, Волк, Шустрый, Бром, Лысый, Охотник и Осведомитель) появляется ветка «есть ли для меня работа?». В целях упрощения создания однотипных квестов, выдаваемых через эту ветку, разработчики создали следующую схему... Структура однотипных квестов
Сами задания находятся в конфиге: gamedata\config\misc\task_manager.ltx
Цели квестов и условия их выполнения фиксируются в скрипте: gamedata\scripts\task_manager.script
Ветки диалогов, отвечающие за эти задания (например, у Сидоровича) выглядит так: <actor_dialog>tm_trader_dialog</actor_dialog> <actor_dialog>tm_trader_reward</actor_dialog>
Где первый actor_dialog – диалог с выдачей задания, а второй - диалог по заданию и получению награды, либо отказа от выполнения оного. Никто нас насильно его выполнять не заставляет.
При получении задания информация об этом прописывается и в PDA, не забываем об этом. Информация добавляется с помощью файла: gamedata\config\gameplay\storyline_info_taskmanager.xml
Описание задания хранится в файле: gamedata\config\text\rus\stable_task_manager.xml
Всего существует шесть видов подобных заданий: eliminate_lager = уничтожить лагерь defend_lager = защитить лагерь kill_stalker = убить сталкер artefact = найти артефакт monster_part = найти часть монстра find_item = найти предмет Создаем квест
Для создания дополнительного квеста у Сидоровича нам понадобятся всего три файла: gamedata\config\gameplay\storyline_info_taskmanager.xml gamedata\config\misc\task_manager.ltx gamedata\config\text\rus\stable_task _manag er.xml
Открываем task_manager.ltx, в начале видим длинный список заданий, а уж потом сами задания и условия выполнения к ним. Т.е. cначала идет: [...] tm_kill_stalker_5 tm_kill_stalker_6 tm_kill_stalker_7 [...]
А затем то, что нам нужно: [...] [tm_kill_stalker_6] type = kill_stalker community = actor text = tm_kill_stalker_6_text description = tm_kill_stalker_6_descr parent = trader target = sim_stalker_novice ;reward_money = 1000 reward_reputation = +3 reward_rank = 2 reward_item = af_vyvert, conserva time = 86400 prior = 1
[tm_kill_stalker_7] type = kill_stalker community = actor text = tm_kill_stalker_7_text description = tm_kill_stalker_7_descr parent = trader target = esc_wolf reward_money = 10000 reward_reputation = +3 reward_rank = 2 reward_item = vodka, conserva time = 86400 prior = 1 [...]
Рассмотрим структуру этих записей, заодно создав новую: [tm_kill_stalker_7] – наше задание type = kill_stalker – тип задания, в данном случае – убийство сталкера community = actor – комьюнити, к которому принадлежит выполняющий задание (странный параметр, в игре используется - везде выставлен равным actor'у) text = tm_kill_stalker_7_text – описание нашего задания в диалоге description = tm_kill_stalker_7_descr – описание нашего задания в PDA parent = trader - заказчик target = esc_wolf – цель, в данном случае мы собираемся грохнуть Волка reward_money = 10000 – сколько денег нам дадут за выполнение reward_reputation = +3 – повышение/понижение репутации после выполнения задания reward_rank = 2 – кличество очков ранга, выдаваемых за выполнение reward_item = vodka, conserva – предметы, выдаваемые в награду за квест time = 86400 – время выполнения квеста prior = 1 – приоритет задания
Не забываем про PDA. В файл: gamedata\config\gameplay\storyline_info_taskmanager.xml
Добавляем следующие строки: <article id="tm_kill_stalker_7_descr" name="kill_stalker" article_type="task"> <text>tm_kill_stalker_7_descr</text> </article>
Текст на русском для задания нужен? Тогда открываем gamedata\config\text\rus\stable_task_manager.xml
И туда вгоняем следующие строки: <string id="tm_kill_stalker_7"> <text>Убить Волка</text> </string> <string id="tm_kill_stalker_7_text"> <text>Достал меня Волк! Понимаешь, достал! Ворует у меня водку, понимаешь, потом идет и клянчит деньги на тушенку, потому что новички его не слушают и есть ничего не дают, а вот сойти со своего места и пойти охотится на кабанов, он, видите ли, не хочет, ибо ЛЕНЬ! Грохни его, Меченый, я в долгу не останусь.</text> </string> <string id="tm_kill_stalker_7_descr"> <text>Сидрыч заказал убийство Волка</text> </string>
Важно! Эти строки не должны выходить за пределы тегов <string_table> и </string_table>.
Вот и все, теперь у Сидоровича появился новый квест - "Убить Волка".
Редактирование костюмов
Разбор параметров
В этой статье мы рассмотрим значения параметров костюмов. Все параметры, не упомянутые ниже, изменять строго не рекомендуется!
Итак, секции с параметрами находятся в файле: config\misc\outfit.ltx
Разберем, например, параметры классического костюма сталкера. [stalker_outfit]:outfit_base
; название секции костюма. именно к нему игра будет обращаться. outfit_base - базовая (наследуемая) секция (изменять строго не рекомендуется).
visual = equipments\stalker_suit ; модель, используемая для сложенного костюма, лежащего на земле.
actor_visual = actors\hero\stalker_hood.ogf ; модель, используемая для игрока, когда на нем надет костюм.
inv_name = stalker_outfit_name ; название в инвентаре.
inv_name_short = stalker_outfit_name ; название в инвентаре (в принципе, можно здесь продублировать значение предыдущего параметра - так чаще всего и делают).
description = stalker_outfit_description ; ссылка на текстовую строку с описанием.
inv_weight = 5.0 ; вес.
; далее идут параметры иконки в инвентаре (см. примечание 1).
[sect_stalker_outfit_immunities] ; коэффициенты иммунитета самого костюма, то есть - то, насколько сильно он сам подвержен повреждениям от различных типов воздействий. по значениям каждого типа - см. выше.
Я думаю каждый из вас натыкался на это файл? Сейчас я объясню, что в нём можно изменить. Смена длительности дня и ночи За это отвечает строка #time_factor = 10; 1; ;396.0; если значение 10 поменять на 1, то время в игре начнёт тянуться как у нас с вами в настоящей жизни (при это будет иметь смысл спать, сидеть у костра, есть и т.п.)) Время и дата старта игры
отвечают за время и дату начала игры (пригодится для модификаций с новым сюжетом) Период автосохранения
Строка autosave_interval = 01:05:00
отвечает за промежуток между автоматическим сохранением игры. Радиус загрузки локации
Строка switch_distance = 150;0 ;150 ;75.0
(Внимание! Лучше не трогать!) отвечает за радиус загрузки локации, надеюсь для вас не секрет, что игра загружает не всё локацию сразу, так вот изменяя первое число, мы изменяем радиус загрузки локации.
Этот параметр также влияет на неписей и мутантов, при увеличении его увеличивается возможность стычек между группировками или мутантами. Например, я поставил значение 300 и на лагерь новичков теперь регулярно нападают военные с блокпоста.
______________________________ Список буду пополнять. Возникнут вопросы задавайте в теме.