В скриптах есть одна единственная функция, отвечающая за спавн объектов:
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,
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’а. Концепция спавна перенесена практически без изменений, поэтому на авторство этого способа спавна я никоим образом не претендую 🙂
Итак, открываем наш пустой файл на редактирование, первой строкой объявляем переменную, в которой хранятся наши зомби:
Далее пишем функцию:
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
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_str 3
В local sect_alias строчку:
zombie_weak = «zombie_weak», zombie_normal = «zombie_normal», zombie_str «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 :
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:
………
escape_trader_talk_info_999