F.A.Q в S.T.A.L.K.E.R.: Shodow Of Chernobyl
реклама
Для того чтобы изменить носимый вес нам необходимо 2 файла это:
gamedata/config/creatures/actor.ltx
gamedata/config/system.ltx
Займемся файлом actor.ltx открываем файл блокнотом, ну или как вам удобно.Жмем F3 ищем строку max_item_mass = 50.0
Изменяем max_item_mass = 500.0
Изменяем max_walk_weight = 500.0
С этим файлом все.
Открываем system.ltx также ищем строку max_weight = 50 и меняем на max_weight = 500.0
Все можем грузить игру и проверять все ли работает.
2. Сделать бессмертие.
Нужен лишь один файл:
gamedata/config/creatures/actor.ltx
Открываем находим секцию [actor_condition] Рассмотрим все детально:
Чтобы сделать бессмертие нужно:
Изменить параметр satiety_v на 0.0; (скорость уменьшения сытости со временем)
radiation_v на 1.0; (скорость уменьшения радиации)
satiety_critical на 0.0; (критическое значения сытости (в процентах от 0..1) когда здоровье начианает уменьшаться)
health_hit_part на 0.0; (процент хита, уходящий на отнимание здоровья)
power_hit_part на 0.0 (процент хита, уходящий на отнимание силы)
max_power_leak_speed на 0.0;(если не изменен)
min_wound_size на 1.0; (минимальный размер раны, после которого она считается зажившей)
------------------------------------------Спавн через скрипт-------------------------------
В скриптах есть одна единственная функция, отвечающая за спавн объектов:
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_verte())
Почему 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,
obj.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, мы добавим лишь визуальное представление.
Пишем дальше:
$spawn= "monsters\zombies\zombie_ghost"
visual= monsters\zombi\zombi_1_ghost
Все. Сохраняем изменения и закрываем файл.
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.
Нам нужно просто проверить работоспособность скриптового спавна, поэтому я приведу просто собственно сам измененный диалог из файла dialogs_escape.xml:
<dialog id="escape_trader_talk_info">
………
<phrase id="999">
<text>escape_trader_talk_info_999</text>
<next>7770</next>
<next>9991</next>
<next>9992</next>
<next>9993</next>
<next>9994</next>
<next>9995</next>
<next>9996</next>
</phrase>
<phrase id="9992">
<text>escape_trader_talk_info_9992</text>
<next>99922</next>
</phrase>
<phrase id="99922">
<text>escape_trader_talk_info_99922</text>
<next>9996</next>
<next>9995</next>
</phrase>
<phrase id="9993">
<text>escape_trader_talk_info_9993</text>
<next>99933</next>
</phrase>
<phrase id="9995">
<text>escape_trader_talk_info_9995</text>
</phrase>
<phrase id="3121">
<text>escape_trader_talk_info_3121</text>
<next>9996</next>
<next>9995</next>
</phrase>
<phrase id="3131">
<text>escape_trader_talk_info_3131</text>
<next>9996</next>
<next>9995</next>
</phrase>
<phrase id="41">
<text>escape_trader_talk_info_41</text>
<next>9996</next>
<next>9995</next>
</phrase>
<!------Наш диалог: Начало------->
<phrase id="7770">
<text>escape_trader_talk_info_7770</text>
<next>7771</next>
</phrase>
<phrase id="7771">
<text>escape_trader_talk_info_7771</text>
<next>7772</next>
<next>7773</next>
</phrase>
<phrase id="7772">
<text>escape_trader_talk_info_7772</text>
<next>7777</next>
</phrase>
<phrase id="7773">
<text>escape_trader_talk_info_7773</text>
<next>7779</next>
</phrase>
<phrase id="7779">
<text>escape_trader_talk_info_7779</text>
<next>9996</next>
<next>9995</next>
</phrase>
<phrase id="7777">
<text>escape_trader_talk_info_7777</text>
<action>esc_zombie.zombie_story_1</action>
<next>9996</next>
<next>9995</next>
</phrase>
<!------Наш диалог: Конец------->
<phrase id="51">
<text>escape_trader_talk_info_51</text>
<next>9996</next>
<next>9995</next>
</phrase>
……
</dialog>
И также связанный с ним файл 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>
Все. Можно запускать игру, идти на Кордон, после разговороа с Сидоровичем, в зависимости от выбранного Меченным решения, бежим на фабрику и … смотрим сами :)
Готовые файлы примера
Spawn Lib
Домашнее задание - вернуть в игру 6-ой тип гражданского зомби :)
Продолжение следует…
Практика (часть 2)
4. Сегодня мы закончим с зомби в полном объеме – добавим их описания в энциклопедию, добавим иконки, и разберемся с «домашним заданием»:) Думаю, что внимательно изучив эту статью, вы сами сможете через скриптовые функции восстановить любого персонажа, не вошедшего в финальный релиз игры. Если у кого хватит времени и желания, те могут даже написать что-то типа «Ночи живых мертвецов» :) - серию квестов, связанную своим собственным сюжетом. Итак, «домашнее задание» - добавляем в игру шестого зомби.
Создаем в папке gamedata папку meshes, в ней папку monsters, а там – папку zombi. В папке meshes хранятся модели персонажей, объектов, окружения, присутствующих в игре. Как я уже говорил выше, в игре представлено 5 моделей гражданских зомби, а вот текстур – 6. Также немного смутила нумерация моделей – 1, 2, 4... А где третий? Наверное, сбежал :)
Скопируем из папки с оригинальными файлами игры в созданную нами папку zombi файл zombi_2.ogf и переименуем его в zombi_3.ogf. Откроем файл нашей новой модели любым HexEditor’ом, я использую BiEd (Binary Editor 1.00). Так как данная статья рассчитана не только на «продвинутых юзеров», но и на «обычных чайников», которые, тем не менее, хотят «что-нить замутить» и при этом не сильно «парить моск», я не буду рассказывать здесь про адресацию, двухбайтовую запись и т.д., я просто наглядно покажу, где что поправить :)
Файл модели до редактирования
Файл модели после редактирования
На скринах видно, что мы просто изменили для этой модели путь к текстуре. Все. Без использования 3D-редакторов и затраты кучи времени на обработку модели в них, мы получили абсолютно новую (на внешний вид) модель. :)
5. Теперь пропишем нашего нового зомби во все файлы, которые мы создали ранее. В файл m_zombie.ltx в самый конец добавляем секцию:
[zombie_old]:zombie_normal
$spawn= "monsters\zombies\zombie_old"
visual= monsters\zombi\zombi_3
в файле esc_zombie.script изменяем массив в первой строке:
local zombie_types = {"zombie_weak", "zombie_normal", "zombie_strong", "zombie_immortal",
"zombie_old", "zombie_ghost"}
В функции spawn_zombies изменяем строку спавна:
alife():create(zombie_types[math.random(6)],new_pos,
db.actor:level_vertex_id(),db.actor:game_vertex_id())
в функции zombie_story_1 меняем число объектов на кратное 6-ти (необязательно):
spawn_zombies( spawn_point, 12 )
Всё. Сохраняем и закрываем.
6. Копируем в папку gamedata\config\gameplay\ файл encyclopedia_mutants.xml, добавляем описание зомби в энциклопедию:
<!-------------------------------- Zombieg ----------------------------->
<article id="mutant_zombieg_general" name="Zombieg" group="Mutants">
<texture>ui_npc_monster_zombieg</texture>
<text>enc_mutant_zombieg_general</text>
</article>
И в связанный с ним файл string_table_enc_mutants.xml в папке gamedata\config\text\rus\ добавляем:
<string id="Zombie">
<text>Зомби, гражданский</text>
</string>
<string id="Zombieg">
<text>Зомби-призрак, гражданский</text>
</string>
<string id="enc_mutant_zombieg_general">
<text>Зомби-привидение отличается от обычного зомби лишь тем, что
воздействие Выжигателя мозгов полностью разрушило не только структуру личности, но и
тело, поэтому зомби-призрак несколько более живуч по сравнению с обычным зомби.</text>
</string>
<string id="enc_mutant_zombie_general">
<text>Воздействие Выжигателя мозгов полностью разрушает структуру личности,
оставляя только телесную оболочку.\n\n Побродив немного по Зоне, лишённые разума тела
начинают превращаться в настоящих зомби. Из рефлексов у них остаются лишь самые примитивные,
оружие и экипировка скоро приходят в негодность. В результате зомби становятся собой ни чем
иным, как медлительными полутрупами, которых наличествуют лишь два эффективных раздражителя:
еда и сон. Зомби совершенно неразборчивы в выборе пищи и питья, поэтому их тела буквально
пропитаны радиацией и токсинами. Как правило, эти существа бесцельно бродят по Зоне или,
словно трупы, валяются внутри заброшенных построек. Однако, лишь только зомби почует близкое
присутствие живого человека, он сразу же пытается атаковать. Умудрённые опытом сталкеры
стараются обходить эти неуклюжие опустошённые оболочки.</text>
</string>
Копируем сюда же файл stable_statistic_caption.xml и изменяем в нем 3 строчки:
<string id="zombie_normal">
<text>зомбированный, гражданский</text>
</string>
<string id="zombie_strong">
<text>зомби-призрак, гражданский</text>
</string>
<string id="zombie_weak">
<text>зомби, гражданский</text>
</string>
Сохраняем и закрываем.
7. И последнее – добавим иконки. Скажу сразу, воспользовался готовым файлом, уже содержащим иконки зомби и других «восстановленных монстров» (спасибо Fr3nzy). Поэтому просто скопируйте файл ui_npc_monster.dds из архива в папку gamedata\textures\ui\, а файл ui_npc_monster.xml – в папку gamedata\config\ui\. Если вы хотите сделать собственные - прочитайте урок по изменению текстур.
Вкратце, что описывает файл ui_npc_monster.xml: в нем задаются координаты иконок, расположенных в файле ui_npc_monster.dds, применительно к каждому типу монстров в игре.
Заключительный штрих. Откройте файл m_zombie.ltx и в первой секции замените строку
icon = ui_npc_monster_kontroler
На:
icon = ui_npc_monster_zombie
В секцию [zombie_ghost] добавьте строку:
icon = ui_npc_monster_zombieg
Сохраняйте изменения. Всё.
Вот готовые файлы примера. Удачи и спасибо за внимание :)
Спавн NPC
Эта часть статьи написана Arhet и создана на примере того, как в SRP Mod были созданы NPC группировки "Грех".
Задействуем файлы:
gamedata\config\gameplay\character_desc_escape.xml
gamedata\config\gameplay\npc_profile.ltx
gamedata\config\creatures\spawn_sections.ltx
Начнем с character_desc_escape.xml. Описание что значит каждая строка писать не буду, так как все до меня уже сделано.
Тут создадим нового персонажа:
<specific_character id="esc_dark_stalker_1" team_default = "1">
<name>GENERATE_NAME_bandit</name>
<icon>ui_npc_dark_1</icon>
<map_icon x="0" y="0"></map_icon>
<bio>sim_stalker_novice_bio</bio>
<class>esc_dark_stalker_01</class> - вот тут наш новый класс кстати.
<community>dark_stalker</community> <terrain_sect>stalker_terrain</terrain_sect>
<money min="200" max="600" infinitive="0"></money>
<rank>198</rank>
<reputation>-24</reputation>
<visual>actors\dark_stalker\dark_stalker_1</visual>
<snd_config>characters_voice\human_01\monolith\</snd_config>
<crouch_type>-1</crouch_type>
<panic_treshold>0</panic_treshold>
<supplies>
[spawn] \n
wpn_dark_colt1911 \n
ammo_11.43x23_hydro \n
#include "gameplay\character_items.xml" \n
#include "gameplay\character_drugs.xml" \n
#include "gameplay\character_food.xml"
</supplies>
#include "gameplay\character_criticals_3.xml"
<start_dialog>dm_hello_dialog</start_dialog>
<actor_dialog>dm_cool_info_dialog</actor_dialog>
</specific_character>
Впишем наш код после какого-нибудь </specific_character>
Теперь идем в npc_profile.xml и туда вгоняем:
<character id="esc_dark_stalker_1">
<class>esc_dark_stalker_01</class>
<specific_character>esc_dark_stalker_1</specific_character>
</character>
Теперь надо заняться spawn_sections.ltx. Скрипт будет «брать NPC» как раз из этого файла. Пишем туда:
[esc_dark_stalker_1]:stalker – имя секции для скрипта..
$spawn = "respawn\esc_dark_stalker_1"
character_profile = esc_dark_stalker_1 – ссылка на описание нашего NPC
spec_rank = regular – спец ранг
community = dark_stalker – группировка.
Теперь берем любой скрипт спавна NPC, вгоняем туда имя секции из spawn_sections.ltx и ву-а-ля.
Тока забыли что данному НПС будет присвоенная дефолтовая логика (т.е. тупо хожу куда сам не знаю) забыли упомянуть про (ххх - лока):
ххх gulag.script (действия НПС! Где можно: где можно присвоить статус кампера; волкера; и.д.т)
ххх gulag.ltx (дублер логики т.е. дублирует кратко действия НПС)
Структура однотипных квестов
Сами задания находятся в конфиге:
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_manager.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>.
Вот и все, теперь у Сидоровича появился новый квест - "Убить Волка".
Создание новых веток диалогов
Теория
1) Списки веток диалогов содержатся в файлах вида gamedata/config/gameplay/character_desc_*.xml
Это, например:
character_desc_zombied.xml character_desc_stalker.xml character_desc_garbage.xml ...
Файлы character_desc_*.xml можно сравнить со стволом дерева диалогов. В них перечисляется названия прикрепляемых веток диалогов
Например вот список веток диалога с Сидоровичем взятый из файла character_desc_escape.xml
<start_dialog>escape_trader_start_dialog</start_dialog>
<actor_dialog>escape_trader_talk_info</actor_dialog>
<actor_dialog>escape_trader_jobs</actor_dialog>
<actor_dialog>tm_trader_dialog</actor_dialog>
<actor_dialog>tm_trader_reward</actor_dialog>
<actor_dialog>escape_trader_done_blockpost_box</actor_dialog>
В свою очередь каждая ветка диалога также может ветвится.
2) Ветвление диалогов прописывается уже в других файлах.
Например, ветвление диалога с Сидоровичем содержится в файле gamedata/config/gameplay/dialogs_escape.xml Возьмем оттуда, например, ветвление escape_trader_jobs.
Ветвление имеет довольно большие масштабы, поэтому приведу только часть:
<dialog id="escape_trader_talk_info">
<precondition>escape_dialog.trader_has_talk_info_wr</precondition>
<has_info>tutorial_end</has_info>
<phrase_list>
<phrase id="1">
<text>escape_trader_talk_info_1</text>
<next>100</next>
<next>99</next>
<next>9995</next>
</phrase>
...
<phrase id="0">
<text>escape_trader_talk_info_0</text>
<next>1</next>
</phrase>
</phrase_list>
</dialog>
Здесь <precondition>…</precondition> - это проверка выполнения условия. Ветка появится в диалоге, только если условие выполняется. Конкретно <precondition>escape_dialog.trader_has_talk_info_wr</precondition> из ветки escape_trader_talk_info - это обращение к функции trader_has_talk_info_wr, находящейся в файле скрипте gamedata/scripts/escape_dialog.script
Функция выглядит так:
function trader_has_talk_info_wr( trader, actor )
return true
end
То есть, судя по его структуре, <precondition>escape_dialog.trader_has_talk_info_wr</precondition> выполняется всегда, т.к. функция всегда возвращает истину и <dialog id="escape_trader_talk_info"> пропускается в списк реплик.
Но для конкретной ветки может быть несколько precondition и других условий.
Далее, <has_info>tutorial_end</has_info> - это еще одна проверка, на этот раз на наличие у игрока так называемых infoportions, выдаваемы в процессе ключевых диалогов. В данном случае это проверка на то, закончена ли определенная стадия туториала, или нет. Т.е. ветка допустится в список реплик если стадия туториала закончена.
Более детально мы это разберем в конце статьи.
А далее идут конкретные фразы, содержащие ссылки на вытекающие фразы, например:
<phrase id="0">
<text>escape_trader_talk_info_0</text>
<next>1</next>
</phrase>
Это основа ветки escape_trader_talk_info.
Важно! В любой основной ветке любого диалога фраза <phrase id="0"> будет основой, из которой далее будет все вытекать. Она должна обязательно присутствовать и в вашем диалоге.
<next>1</next> - это ссылка на вытекающую фразу <phrase id="1">:
<phrase id="1">
<text>escape_trader_talk_info_1</text>
<next>100</next>
<next>99</next>
<next>9995</next>
</phrase>
В свою очередь <next>100</next>, <next>99</next>, <next>9995</next> это ссылки на фразы веточки растущие из фразы <phrase id="1">.
3) Текст каждой фразы содержится в третьем файле. Для диалога с Сидоровичем тексты лежат в файле gamedata/config/text/rus/stable_dialogs_escape.xml
<string id="escape_trader_talk_info_0">
<text>Есть несколько вопросов.</text>
</string>
<string id="escape_trader_talk_info_1">
<text>Спрашивай, только я ведь всего не знаю. Сам понимаешь, сижу тут
целыми днями, а жизнь - она вся там, снаружи, в Зоне. Могу рассказать о Зоне вообще, а немного
могу о ближайших окрестностях, где сам ходил.</text>
</string>
...
Эти строки содержат тексты для фраз <phrase id="0"> и <phrase id="1">
Итого диалоги разложены по трем, а то и более файлам.
Да кстати, путь по веткам может быть зацикленным, если того требует диалог. Например так:
<phrase id="0">
<text>...</text>
<next>1</next>
<next>2</next>
</phrase>
<phrase id="1">
<text>...</text>
<next>11</next>
<next>12</next>
</phrase>
<phrase id="11">
<text>...</text>
<next>1</next> - Это возврат к фразе №1 (зацикливание)
<next>111</next>
</phrase>
Практика
Добавим в диалог с Сидоровичем ветку своего собственного изготовления.Например такую:
Меченый: Сидрыч, а чего это у тебя зеленые человечки, что по столу бегают, такие худые?
Сидорович: Чего?!
Меченый: Ты их совсем, совсем не кормишь?
Сидорович: В следующий раз, как пойдешь в зону, бери-ка вместо водяры побольше антирада. А то
мало что таким перегаром дышишь, уже до зеленых человечков долечился... Шутник.
Для этого:
1) В файле gamedata/config/gameplay/character_desc_escape.xml в конце списка веток для trader припишем свою ветку с произвольным названием. Это будет, например, <actor_dialog>escape_trader_letat_gusi</actor_dialog>.
Т.е у нас получится так:
<specific_character id="escape_trader" no_random = "1">
...
<start_dialog>escape_trader_start_dialog</start_dialog>
<actor_dialog>escape_trader_talk_info</actor_dialog>
<actor_dialog>escape_trader_jobs</actor_dialog>
<actor_dialog>tm_trader_dialog</actor_dialog>
<actor_dialog>tm_trader_reward</actor_dialog>
<actor_dialog>escape_trader_done_blockpost_box</actor_dialog>
<actor_dialog>escape_trader_letat_gusi</actor_dialog>
</specific_character>
…
Записываем изменения, с этим файлом пока всё.
2) Теперь берем файл gamedata/config/gameplay/dialogs_escape.xml
Диалогу:
Меченый: Сидрыч а почему это у тебя зеленые человечки, что по столу бегают, такие худые?
Сидорович: Чего?!!
Меченый: Ты их совсем, совсем не кормишь?
Сидорович: В следующий раз, как пойдешь в зону, бери-ка вместо водяры побольше антирада. А то
мало что таким перегаром дышишь, уже до зеленых человечков долечился... Шутник.
Будет соответствовать такая структура:
<phrase id="0">
<text>escape_trader_letat_gusi_0</text>
<next>1</next>
</phrase>
<phrase id="1">
<text>escape_trader_letat_gusi_1</text>
<next>2</next>
</phrase>
<phrase id="2">
<text> escape_trader_letat_gusi_2</text>
<next>3</next>
</phrase>
<phrase id="3">
<text> escape_trader_letat_gusi_3</text>
</phrase>
Условия наличия ветки в диалоге можно взять из ветки <dialog id="escape_trader_talk_info">.
Т.е берем условия <precondition>escape_dialog.trader_has_talk_info_wr</precondition> и <has_info>tutorial_end</has_info>. Можно было, конечно, прописать в скрипте еще одно условие для ветки, чтобы она появилась только один раз, а потом больше не возникала. Но об этом как-нибудь позже.
В итоге у нас получилась такая структура:
<dialog id="escape_trader_letat_gusi">
<precondition>escape_dialog.trader_has_talk_info_wr</precondition>
<has_info>tutorial_end</has_info>
<phrase_list>
<phrase id="0">
<text>escape_trader_letat_gusi_0</text>
<next>1</next>
</phrase>
<phrase id="1">
<text>escape_trader_letat_gusi_1</text>
<next>2</next>
</phrase>
<phrase id="2">
<text> escape_trader_letat_gusi_2</text>
<next>3</next>
</phrase>
<phrase id="3">
<text> escape_trader_letat_gusi_3</text>
</phrase>
</phrase_list>
</dialog>
Её нужно вставить в любом месте между dialog id'ами других веток в файле dialogs_escape.xml. Главное - не промахнутся и засунуть именно между, а не внутрь одного из dialog id.
После сохранения внесенных изменений с файлом dialogs_escape.xml все.
3) Теперь вбиваем сами текстовички в файле gamedata/config/text/rus/stable_dialogs_escape.xml
Т.е нам надо в файле stable_dialogs_escape.xml вставить такую конструкцию:
<string id="escape_trader_letat_gusi_0">
<text>Сидрыч, а чего это у тебя зеленые человечки, что по столу бегают, такие худые?</text>
</string>
<string id="escape_trader_letat_gusi_1">
<text>Чего?!</text>
</string>
<string id="escape_trader_letat_gusi_2">
<text>Ты их совсем, совсем не кормишь?</text>
</string>
<string id="escape_trader_letat_gusi_3">
<text>В следующий раз, как пойдешь в зону, бери-ка вместо водяры побольше
антирада. А то мало что таким перегаром дышишь, уже до зеленых человечков долечился...
Шутник.</text>
</string>
В любом месте между уже существующими string id. После сохранения изменений, у нас все готово. Можно загружать игру и смотреть что получилось.
Дополнительно
Внимание! После патча 1.002 данный урок перестал работать. Причина - со вторым патчем несовместимо это условие:
<precondition>escape_dialog.trader_has_talk_info_wr</precondition>
Дело в том, что во втором патче из файла escape_dialog.script была удалена функция:
function trader_has_talk_info_wr( trader, actor )
return true
end
Можно либо записать эту функцию обратно в escape_dialog.script, либо использовать другие более-менее подходящие условия, например:
<precondition>escape_dialog.trader_alredy_give_job</precondition>
Всё. Мы научились писать простые диалоги.
Создание квестов и диалогов
Создание базового диалога
Итак, приступим к созданию диалога, в котором нам будет выдаваться новый квест. Участвующие лица: Сидорович (С) и Меченый (М)
М: Здравствуй, Сидорыч! Есть работенка?
С: О, неужели заглянул к нам на огонек, Меченый? Ладно... ближе к телу... эээ, к делу. У меня к тебе следующая просьба: сбегай к Волку, у него что-то намечалось.
М: Без проблем! Уже бегу.
Будем считать, определились с фразами базового диалога. Теперь необходимо добавить его в соответствующем виде, чтобы игре он был «понятен».
Создание «скелета» диалога.
Для того, чтобы добавить наш диалог, необходимо открыть для редактирования файл gamedata\config\gameplay\dialogs_escape.xml
Для тех, кто знаком с языком XML структура данного файла будет ясна с первого взгляда. Но, поподробнее об этом.
Для того, чтобы добавить в игру диалог предусмотрены так называемые тэги. В данном случае, тэгами являются такие конструкции:
1. <game_dialogs> и закрывающий к нему - </game_dialogs>
2. <dialog id = ""> закрывающий - </dialog>
3. <phrase_list> - </phrase_list>
4. <phrase id = ""> - </phrase>
5. <text> </text>
6. <next> </next>
В принципе, этих конструкций уже достаточно для того, чтобы создать диалог. Но, диалог, созданный на основе этих конструкций, не сможет стать основой для нового квеста. Для того, чтобы во время диалога вам выдавался квест, есть соответствующий тэг. Но об этом – чуть позже.
Поясню, что обозначает каждый тэг в отдельности.
1. Определяет содержимое файла (в данном случае, говорит о том, что файл содержит диалоги);
2. Определяет новый диалог с id = "". Где содержимое кавычек есть идентификатор диалога. Идентификатор диалога задается в файлах типа character_description_***.xml ;
3. Этот тэг говорит XML-парсеру игры о том, что далее следуют фразы диалога. Иными словами, между <phrase_list> и </phrase_list> содержатся фразы диалога;
4. Этот тэг определяет фразу с id = "". Внутри кавычек содержится идентификатор фразы (ВНИМАНИЕ: идентификатором в данном случае могут быть ТОЛЬКО цифры);
5. Внутри этих двух тэгов содержится ссылка на соответствующий данной фразе текст (пример, esc_trader_talk_info_123). Сам текст берется из файлов типа stable_dialogs_*****.xml, в котором содержатся все фразы в текстовом виде. О создании текста фразы поговорим позже;
6. Внутри этого тэга содержится ссылка (в числовом формате) на следующую (или следующие) фразы.
Теперь приступим к тэгам, позволяющим производить какие-то действия во время диалога.
<precondition> </precondition>. Представляет собой предусловие, в случае выполнения которого (то есть предусловие вернуло ИСТИНУ) данный диалог, или фраза будут появляться в списке доступных во время игры у NPC.
<action> </action>. Данный тэг позволяет производить какое-либо действие во время диалога (например, передачу предметов между общающимися... так же позволяет производить более сложные действия). Внутри тэга располагается ссылка на какую-либо функцию (например, dialogs. actor_set_dolg сделает игрока членом группировки Долг).
<give_info> </give_info>. Позволяет выдавать игроку во время диалога так называемые info_portion'ы (кстати, инфопоршны можно выдавать так же и через <action>, только это потребует создания специальной функции). info_portion – является основой для квестов, info_portion’ы определяются в файлах типа info****.xml. Инфопоршны могут, как начинать квест, так и заканчивать (завершать) его. Так же, с помощью инфопоршнов можно отслеживать действия игрока, для того, чтобы, например, вывести в нужное время диалог. <has_info> </has_info> и <dont_has_info> </dont_has_info> являются предусловиями для текущей фразы/диалога. Первый тэг проверяет наличие данного инфопоршна у игрока, второй – отсутствие.
Ну что, теперь мы можем создать диалог, в котором нам будет выдаваться квест. Но мы не создали самих info_portion'оф, которые позволяет активировать квест.
Создание info_portion'ов
Итак, открываем файл gamedata\config\gameplay\ info_l01escape.xml. Что мы там видим? Правильно, все ту же структуру XML. В данном файле нас интересуют только два тэга:
<info_portion id = ""> </info_portion>
<task> </task>
Первый объявляет сам info_portion. Второй – объявляет задание (идентификатор задания id содержится внутри тэга <task>)
Для примера, создадим 3 info_portion, которые понадобятся нам для будущего квеста:
<info_portion id = "new_task_started">
<task>new_task</task>
</info_portion>
Это инфопоршн с именем new_task_started. Он активирует квест с именем new_task.
<info_portion id = "player_talked_with_wolf">
</info_portion>
<info_portion id = "player_complete_new_task">
</info_portion>
Создание нового квеста
Сами квесты содержаться в файлах типа tasks_*****.xml
Откроем файл gamedata\config\gameplay\tasks_escape.xml. Структура – все тот же XML.
Для примера исследуем уже существующий квест:
<game_task id="esc_help_wounded_from_raid" prio="485">
<title>esc_help_wounded_from_raid</title>
<objective>
<text>esc_help_wounded_from_raid_0</text>
<icon>ui_iconsTotal_esc_help_wounded_from_raid</icon>
<function_complete>escape_tasks.task_fox_complete</function_complete>
<infoportion_set_complete>garbage_meetstalker_start</infoportion_set_complete>
<article>esc_fox_help</article>
</objective>
<objective>
<text>esc_help_wounded_from_raid_1</text>
<map_location_type hint="esc_fox">green_location</map_location_type>
<object_story_id>Escape_stalker_from_raid</object_story_id>
<infoportion_complete>escape_fox_heal</infoportion_complete>
<infoportion_fail>esc_dogs_return</infoportion_fail>
</objective>
<objective>
<text>esc_help_wounded_from_raid_2</text>
<map_location_type hint="esc_fox">green_location</map_location_type>
<object_story_id>Escape_stalker_from_raid</object_story_id>
<infoportion_complete>escape_stalker_done</infoportion_complete>
</objective>
</game_task>
По кусочкам:
<game_task id="esc_help_wounded_from_raid" prio="485">
</game_task>
Задает имя (id) нового квеста. В данном случае, esc_help_wounded_from_raid. prio = "" задает приоритет задачи. Чем выше приоритет, тем больше вероятность того, что текущий маркер квеста будет переключен на ваше задание.
<title>esc_help_wounded_from_raid</title>
Задает заголовок квеста. Т. е. его название. Название можно прописать в виде текста, либо в виде ссылки. В данном случае используется ссылка на текст, который хранится в файле string_table_tasks_escape.xml.
<function_complete>escape_tasks.task_fox_complete</function_complete>
Вызывает функцию из файла escape_tasks.script под именем task_fox_complete. При выполнении данной функции дается info_portion, который завершает данную подзадачу, в случае, если все условия функции соблюдаются.
<infoportion_complete>escape_fox_heal</infoportion_complete>
Текущее подзадание будет завершено в случае, если игроку будет дан этот info_portion.
<infoportion_fail>esc_dogs_return</infoportion_fail>
Текущее подзадание будет провалено в случае, если игроку будет дан этот info_portion.
<map_location_type hint="esc_fox">green_location</map_location_type>
Создает указатель на карте с подсказкой esc_fox и типом green_location.
<object_story_id>Escape_stalker_from_raid</object_story_id>
Указывает на sid из файла gamedata\config\game_story_ids.ltx
<infoportion_set_complete>garbage_meetstalker_start</infoportion_set_complete>
Автоматически выдаёт этот info_portion, если функция, указанная в поле <function_complete> вернула истину, то есть подзадание выполнено.
<text>esc_help_wounded_from_raid_0</text>
Содержит описание подзадачи. Может содержать внутри как текст, так и ссылку на текст.
Создадим задание, которое потребуется нам для диалога:
<game_task id="new_task">
<title>Поговорить с Волком</title>
<objective>
<text>Вернуться к Сидорычу</text>
<map_location_type hint="escape_trader">blue_location</map_location_type>
<object_story_id>Escape_Trader</object_story_id>
<infoportion_complete>player_complete_new_task</ infoportion_complete>
</objective>
<objective>
<text>Поговорить с Волком</text>
<icon>ui_iconsTotal_find_item</icon>
<map_location_type hint="volk">green_location</map_location_type>
<object_story_id>Escape_novice_lager_volk</object_story_id>
<infoportion_complete>player_talked_with_wolf</ infoportion_complete>
</objective>
<objective>
<text>Вернуться к Сидорычу</text>
<map_location_type hint="escape_trader">blue_location</map_location_type>
<object_story_id>Escape_Trader</object_story_id>
<infoportion_complete>player_complete_new_task</ infoportion_complete>
</objective>
</game_task>
Здесь же необходимо обязательно указать ссылку на иконку из файла ui_iconstotal, иначе при попытке заглянуть в раздел "Задания" в ПДА при взятии квеста будет происходить вылет. Для примера я проставил иконку поиска предмета.
Теперь создадим скелеты диалогов для нашего квеста. Потребуется три: один для Волка и два для Сидорыча.
<dialog id = "volk_new_quest">
<has_info>new_task_started</has_info>
<dont_has_info>player_talked_with_wolf</dont_has_info>
<dont_has_info> player_complete_new_task </dont_has_info>
<phrase_list>
<phrase id = "0">
<text>esc_volk_new_quest_0</text>
<next>1</next>
</phrase>
<phrase id = "1">
<text>esc_volk_new_quest_1</text>
<give_info>player_talked_with_wolf</give_info>
<next>2</next>
</phrase>
<phrase id = "2">
<text>esc_volk_new_quest_2</text>
<action>dialogs.break_dialog</action>
</phrase>
</phrase_list>
</dialog>
<dialog id = "esc_trader_new_quest">
<dont_has_info>player_complete_new_task</dont_has_info>
<dont_has_info>player_talked_with_wolf</dont_has_info>
<phrase_list>
<phrase id = "0">
<text>esc_trader_new_quest_0</text>
<next>1</next>
</phrase>
<phrase id = "1">
<text>esc_trader_new_quest_1</text>
<give_info>new_task_started</give_info>
<next>2</next>
</phrase>
<phrase id = "2">
<text>esc_trader_new_quest_2</text>
<action>dialogs.break_dialog</action>
</phrase>
</phrase_list>
</dialog>
<dialog id = "esc_trader_new_quest_complete">
<dont_has_info>player_complete_new_task</dont_has_info>
<phrase_list>
<phrase id = "0">
<text>esc_trader_new_quest_complete_0</text>
<next>1</next>
</phrase>
<phrase id = "1">
<text>esc_trader_new_quest_complete_1</text>
<give_info>player_complete_new_task</give_info>
<next>2</next>
</phrase>
<phrase id = "2">
<text>esc_trader_new_quest_complete_2</text>
<action>dialogs.break_dialog</action>
</phrase>
</phrase_list>
</dialog>
Все «скелеты» добавляем в файл dialogs_escape.xml. Между <game_dialogs> и <dialog id=...>!
Внимательно следите за правильностью написания тэгов и ОДИНАКОВЫМ написанием идентификаций - как в файле с вашим новым диалогом, так и во всех других файлах, где используются тэги или ваши идентификации(например, идентификации фраз в файле с диалогом должны быть такими же, как и идентификации этих же фраз в файле с текстом). Иначе кроме серии вылетов с не всем понятными логами вы ничего не получите. У меня самого были проблемы при попытке активировать квест, выложеный в этой статье, так что пришлось кое-что поправить - и в тэгах, и в идентификаторах.
Примечание: посмотрите на уже готовые диалоги, которые создали разработчики игры – это облегчит вам жизнь.
Теперь бежим в файл character_desc_escape.xml
Ищем там NPC с id = "escape_trader" и добавляем два новых <actor_dialog></actor_dialog>:
<actor_dialog>esc_trader_new_quest_complete </actor_dialog>
<actor_dialog>esc_trader_new_quest </actor_dialog>
Там же ищем NPC с id = "esc_wolf" и добавляем один актор_диалог:
<actor_dialog> volk_new_quest </actor_dialog>
Примечание: <actor_dialog> является диалогом, который активируется самим игроком по желанию. А <start_dialog> активируется только при выполнении определенных условий, заданных в нем, и обычно недоступен.
Осталось только добавить текст трех диалогов, чтобы наш квест стал полноценным. Открываем файл stable_dialogs_escape.xml и добавляем в него новый текст в таком виде:
<string id="esc_trader_new_quest_complete_0">
<text>Все, я выполнил поручение Волка!</text>
</string>
<string id="esc_trader_new_quest_complete_1">
<text>Молодец, Меченый. На тебя можно положиться.</text>
</string>
<string id="esc_trader_new_quest_complete_2">
<text>Рад был помочь, Сидорыч.</text>
</string>
<string id="esc_trader_new_quest_0">
<text> Здравствуй, Сидорыч! Есть работенка?</text>
</string>
<string id="esc_trader_new_quest_1">
<text>О, неужели заглянул к нам на огонек, Меченый? Ладно… ближе к телу… эээ, к делу. У меня к тебе следующая
просьба: сбегай к Волку, у него что-то намечалось.</text>
</string>
<string id=" esc_trader_new_quest_2">
<text>Без проблем! Уже бегу.</text>
</string>
<string id="esc_volk_new_quest_0">
<text>Здорово, Волк. Меня тут Сидорыч послал, говорит у тебя работа есть.</text>
</string>
<string id="esc_volk_new_quest_1">
<text>Привет, Меченый. Верь ты ему больше, этому старому маразматику! Все спокойно сейчас. Так что иди к нему и
скажи, что все хорошо.</text>
</string>
<string id="esc_volk_new_quest_2">
<text>Все, ухожу.</text>
</string>
Все! Поздравляю! Вы только что написали новый квест.
Вступление
Перед изучением этого урока советую освоить находящийся несколько выше урок "Создание диалогов".
Небольшие правила, следуя которым, можно сберечь себе много нервов:
- всегда сохраняйте резервные копии модифицируемых файлов - старайтесь почаще тестировать внесенные изменения - так проще понять, в чем вы ошиблись (если активно работать пару часов без остановки, не разу не протетировав внесенные изменения, то потом будет очень сложно понять - почему же, черт возьми, игра вылетает при попытке поговорить с Сидоровичем?) - перед началом работы создайте новую папку gamedata, а свои моды спрячьте подальше: работать с "чистой" версией игры не в пример удобнее и проще; особенно противопоказан Repair Mod, который с новыми диалогами часто конфликтует. О том, как создать диалоги и квесты, исключающие возможность конфликта с другими подобными модами, см. часть 4.
Создание базового диалога
Начнем создание квеста с написания небольшого линейного диалога.
Открываем файл gamedata\config\gameplay\character_desc_escape.xml и проводим такие манипуляции:
- находим строку "<actor_dialog>escape_trader_done_blockpost_box</actor_dialog>" - добавляем после нее новую - "<actor_dialog>escape_trader_oops</actor_dialog>" - сохраняем изменения
Мы добавили к Диалогу с Сидоровичем новую ветку, вернее, только ссылку на неё. Сама внутренняя структура ветки хранится в файле gamedata\config\gameplay\dialogs_escape.xml, который мы открываем и производим:
- находим в любом месте файла промежуток между </dialog> и <dialog id="..."> - засовываем туда такую конструкцию:
<dialog id="escape_trader_oops">
<precondition>escape_dialog.trader_alredy_give_job</precondition>
<has_info>tutorial_end</has_info>
<phrase_list>
<phrase id="0">
<text>escape_trader_oops_0</text>
<next>1</next>
</phrase>
<phrase id="1">
<text>escape_trader_oops_1</text>
<next>2</next>
</phrase>
<phrase id="2">
<text>escape_trader_oops_2</text>
<next>1</next>
</phrase>
<phrase id="3">
<text>escape_trader_oops_3</text>
</phrase>
</phrase_list>
</dialog>
- сохраняем изменения
Теперь у нас прописана структура новой ветки диалога. Осталось сделать еще одну вещь - прописать строки, которые будут отображаться вместо "escape_trader_oops_...".
Для этого откроем файл gamedata\config\text\rus\stable_dialogs_escape.xml и сделаем следующее:
- находим любой удобный промежуток между </string> и <string id="..."> - прописываем туда следующие строки:
<string id="escape_trader_oops_0">
<text>Сидрыч, давай поговорим о... водке.</text>
</string>
<string id="escape_trader_oops_1">
<text> водке? Да какая водка? Нет её у меня, кончилась. Вроде у Волка на чердаке была, что-то он
однажды об этом упоминал... Принеси, а?</text>
</string>
<string id="escape_trader_oops_2">
<text>Да сбегаю я сейчас, сбегаю... алкоголик, блин.</text>
</string>
<string id="escape_trader_oops_3">
<text>Беги, беги... сил уж нет ждать.</text>
</string>
- сохраняем изменения
Всё. Диалог готов. Можно грузить игру и смотреть, работает ли новая ветка. А теперь будем прикреплять к этому безобразию квест ""Принеси водки Сидоровичу".
Создание базового квеста
Активацию квеста в диалог прикрепить достаточно просто. Проводим с файлом gamedata\config\gameplay\ dialogs_escape.xml следующие манипуляции:
- добавляем содержимое фразы <phrase id="2"> следующим:
<phrase id="2">
<text>escape_trader_oops_2</text>
<give_info>kvest_vodka_started</give_info>
<next>3</next>
</phrase>
- сохраняем изменения
Теперь после фразы Меченого "Да сбегаю я сейчас, сбегаю..." Сидорович таки всучивает нам квест "Принеси водку". Но не спешите тестировать изменения. Мы добавили в диалог упоминание о квесте, хотя он еще не создан.
Исправим это недоразумение. Откройте файл gamedata\config\gameplay\tasks_escape.xml и в начале файла создайте такую конструкцию:
<game_task id="esc_test_vodka_task">
<title>Принести водки</title> <!-- заголовок квеста -->
<objective>
<text>Принести водки Сидоровичу</text>
<icon>ui_iconsTotal_find_item</icon>
<infoportion_complete>test_quest_vodka_otdana</infoportion_complete> </objective>
<objective>
<text>Вернуться с водкой обратно</text>
<map_location_type hint="esc_dinamit_to_volk">green_location</map_location_type>
<object_story_id>Escape_novice_lager_volk</object_story_id>
<infoportion_complete>test_quest_vodka_otdana</infoportion_complete>
</objective>
<objective>
<text>Найти водку на чердаке</text>
<map_location_type hint="escape_trader">blue_location</map_location_type>
<object_story_id>Escape_Trader</object_story_id>
<infoportion_complete>test_quest_vodka_otdana</infoportion_complete>
</objective>
</game_task>
Мы прописали данные о квесте: название, иконку, строки заданий, расположение маркеров и т.д.
К этому файлу мы еще вернемся, т.к. в этом варианте многое проставлялось не иначе как от балды. Я еще подумаю, как заставить задание "Вернуться с водкой обратно" появиться в PDA, а то оно что-то не желает явить себя народу... впрочем, ладно, главное, что все работает и не вылетает. А исправление столь мелких багов - дело десятое. Продолжим.
Откроем файл gamedata\config\gameplay\info_l01escape.xml и зарегистрируем до того отсутствовавшие в игре типы условий (они нам еще пригодятся для завершения работы над квестом):
- после строки <game_information_portions> прописываем:
<info_portion id="test_quest_vodka_otdana"></info_portion>
<info_portion id="kvest_vodka_started">
<task>esc_test_vodka_task</task>
</info_portion>
<info_portion id="otkazalsya_prinesty_vodku"></info_portion>
С прописыванием условий - всё.
Теперь займемся одной небольшой, но нужной вещью - прописыванием экранных подсказок, которые высвечиваются при получении/выполнении этапа/завершении задания.
Для этого откроем файл gamedata\config\text\rus\string_table_tasks_escape.xml и пропишем там:
<string id="esc_test_prinesti_vodku">
<text>Принести бутылку водки</text>
</string>
<string id="esc_test_prinesti_vodku_1">
<text>Найти водку на чердаке у Волка</text>
</string>
<string id="esc_test_prinesti_vodku_2">
<text>Принести водку Сидоровичу</text>
</string>
Теперь можно тестировать, что получилось. Сверяем результаты:
- по окончанию разговора про водку у вас появляется новое задание - в задании маркером отмечен Волк (ближайший к чердаку маркер, потому и отмечен) - при получении задания на экране высвечивается уведомление
Можно приступать к третьей стадии. Что мы забыли? Правильно - возможность выполнить квест.
Реализация возможности выполнить квест
На самом деле - все просто. Все условия и т.д. уже нами созданы, осталось только применить их в диалоге. Давайте несколько изменим его структуру, сделаем его ветвистым и функциональным:
0. Сидрыч, давай поговорим о... водке. ( переход к 1)
1. О водке? Да какая водка? Нет её у меня, кончилась. Вроде у Волка на чердаке была, что-то он однажды об этом упоминал... Принеси, а? ( выбор - переход к 2, 4, 5; изначально доступен только 2 вариант )
2. Да сбегаю я сейчас, сбегаю... алкоголик, блин. ( получение квеста, переход к 3, после этого перестает быть доступен )
3. Беги, беги... сил уж нет ждать. ( конец, переход к списку веток Сидоровича )
4. Вот она, твоя бутылка водки. Ну, вернее, не твоя, а Волка... ( появляется только при наличии водки в инвентаре, при выборе водка передается Сидоровичу, квест выполняется, идет переход к 7 )
5. Извини, Сидрыч, я пока еще не раздобыл, терпи... ( появляется только если взять квест, переход к 6 )
6. А ну марш отсюда! Без бутылки пришел... ууу, вражина... ( переход к 8 )
7. Теперь моя будет, Меченый! Ты так Волку и передай. Спасибо, выручил... держи награду. ( выдача награды, переход к 8 )
8. Ну тебя в пень... ( конец, переход к списку веток Сидоровича )
А вот так это будет выглядеть в файле gamedata\config\gameplay\dialogs_escape.xml:
<dialog id="escape_trader_oops">
<precondition>escape_dialog.trader_alredy_give_job</precondition>
<has_info>tutorial_end</has_info>
<phrase_list>
<phrase id="0">
<text>escape_trader_oops_0</text>
<next>1</next>
</phrase>
<phrase id="1">
<text>escape_trader_oops_1</text>
<next>2</next>
<next>4</next>
<next>5</next>
</phrase>
<phrase id="2">
<dont_has_info>kvest_vodka_started</dont_has_info>
<text>escape_trader_oops_2</text>
<give_info>kvest_vodka_started</give_info>
<next>3</next>
</phrase>
<phrase id="3">
<text>escape_trader_oops_3</text>
<action>dialogs.break_dialog</action>
</phrase>
<phrase id="4">
<text>escape_trader_oops_4</text>
<precondition>escape_dialog.have_a_vodka</precondition>
<action>escape_dialog.otday_vodku</action>
<next>7</next>
</phrase>
<phrase id="5">
<has_info>kvest_vodka_started</has_info>
<text>escape_trader_oops_5</text>
<next>6</next>
</phrase>
<phrase id="6">
<text>escape_trader_oops_6</text>
<next>8</next>
</phrase>
<phrase id="7">
<text>escape_trader_oops_7</text>
<give_info>test_quest_vodka_otdana</give_info>
<action>escape_dialog.transfer_deneg</action>
<next>8</next>
</phrase>
<phrase id="8">
<text>escape_trader_oops_8</text>
</phrase>
</phrase_list>
</dialog>
Теперь добавим в игру скрипты "передача денег" и "передача водки". Откройте файл gamedata\scripts\escape_dialog.script и перед разделителем "Trader" добавьте туда следующие строки:
function transfer_deneg(first_speaker, second_speaker)
dialogs.relocate_money(second_speaker, 4000, "in")
end
function otday_vodku (npc, actor)
dialogs.relocate_item_section(npc, "vodka", "out")
end
function have_a_vodka (stalker, player)
return stalker:object ("vodka") ~= nil
end
Готово. Запускайте игру и любуйтесь.
Для тех, кто еще с нами - сейчас мы модифицируем квест так, чтобы исключить конфликты с другими подобными модами.
Реализация совместимости
Из-за чего наш мод не может работать вместе с аналогичными ему модами? Да потому, что файлы dialogs_escape.xml и некоторые другие у них пересекаются. Поэтому выделим их отдельно от основных диалогов:
Вместо dialogs_escape.xml мы будем испольховать новый - dialogs_new.xml Вместо stable_dialogs_escape.xml мы будем использовать файл stable_dialogs_new.xml (важно! не знаю почему, но он должен лежать в text\..., а не в text\rus\...)
Соответственно, весь реализованный нами новый контент перекидываем в пустые файлы (соблюдая форматирование), подчистив за собой на старом месте.
Единственное, что останется сделать после установки того же Repair Mod'а - заново добавить ветку диалога Сидоровичу в файле character_desc_escape.xml. Остальное в новом варианте нашего мода переписывать не придется - для диалогов отведено три файла.
Остальные файлы относятся уже к квестовой части и ни с чем пересекаться не могут - где вы видели другие моды с новыми квестами? Я - нет.
Откройте файл gamedata\config\localization.ltx и зарегистрируйте там stable_dialogs_escape.ltx. Потом откройте файл gamedata\config\system.ltx и в разделе "Dialogs" в конце строки (после dialogs_aes) пропишите dialogs_new (без ".ltx"!).
Все. Теперь ваши диалоги будут подгружаться из сторонних файлов.
Если изменить шапку нашего диалога в файле dialogs_escape.xml со старого варианта на такой, то ветка исчезнет после выполнения задания:
<dialog id="escape_trader_oops">
<precondition>escape_dialog.trader_alredy_give_job</precondition>
<has_info>tutorial_end</has_info>
<dont_has_info>test_quest_vodka_otdana</dont_has_info>
<phrase_list>
(c) XiaNi; Keha.
реклама
Лента материалов
Соблюдение Правил конференции строго обязательно!
Флуд, флейм и оффтоп преследуются по всей строгости закона!
Комментарии, содержащие оскорбления, нецензурные выражения (в т.ч. замаскированный мат), экстремистские высказывания, рекламу и спам, удаляются независимо от содержимого, а к их авторам могут применяться меры вплоть до запрета написания комментариев и, в случае написания комментария через социальные сети, жалобы в администрацию данной сети.
Сейчас обсуждают