Спавн через скрипт — различия между версиями — S.T.A.L.K.E.R. Inside Wiki

Спавн через скрипт — различия между версиями

Материал из S.T.A.L.K.E.R. Inside Wiki

Перейти к: навигация, поиск
м (Откат правок 77.37.156.108 (обсуждение) к версии 188.187.20.8)
 
(не показаны 35 промежуточные версии 14 участников)
Строка 2: Строка 2:
  
 
В скриптах есть одна единственная функция, отвечающая за спавн объектов:
 
В скриптах есть одна единственная функция, отвечающая за спавн объектов:
<pre>alife():create(section,position,levelvertex,gamevertex)</pre>
 
  
<span style="color: gray">Строго говоря, их две: create create_ammo но различия между ними не существенны. [[Участник:Imp|Imp]] 22:45, 23 июля 2007 (EEST)</span>
+
<lua>
 +
alife():create(section,position,levelvertex,gamevertex)
 +
</lua>
  
Первый параметр - секция в конфигурациях, описывающая объект, например "bolt","med_kit" - это простые секции, простых объектов
+
<span style="color: gray">Строго говоря, их две: create и create_ammo но различия между ними не существенны. [[Участник:Imp|Imp]] 22:45, 23 июля 2007 (EEST)</span>
 +
 
 +
Первый параметр - секция в конфигурациях, описывающая объект, например "bolt","medkit" - это простые секции, простых объектов
 
а есть объекты, которые переходят в онлайн/оффлайн, это неписи, монстры и так далее, например mil_killer_respawn_2 - спавнится снайпер группировки киллеров.
 
а есть объекты, которые переходят в онлайн/оффлайн, это неписи, монстры и так далее, например mil_killer_respawn_2 - спавнится снайпер группировки киллеров.
  
 
С позицией, думаю объяснять не надо, только существует нюанс - высота это Y, а не Z.
 
С позицией, думаю объяснять не надо, только существует нюанс - высота это Y, а не Z.
Задать позицию можно такой конструкцией vector():set(x,y,z), где x, y и z - координаты точки на уровне, где спавним объект.
+
 
 +
[http://images.gameru.net/image/direct/5e560ebeb5.jpg Направления в пространстве]
 +
 
 +
Задать позицию можно такой конструкцией: vector():set(x,y,z), где x, y и z - координаты точки на уровне, где спавним объект.
  
 
Вертекс в игре Сталкер не тоже самое, что вертекс в трехмерной графике. Vertex - определенная зона малого размера за которой закреплен индивидуальный номер в пределах уровня или целой игры.  
 
Вертекс в игре Сталкер не тоже самое, что вертекс в трехмерной графике. Vertex - определенная зона малого размера за которой закреплен индивидуальный номер в пределах уровня или целой игры.  
Строка 18: Строка 24:
 
game_vertex_id нужен для того, чтобы указать на какой карте спавнить объект.
 
game_vertex_id нужен для того, чтобы указать на какой карте спавнить объект.
  
Соответственно, чтобы заспавнить что-нибудь на другой карте, достаточно указать game_vertex_id в четвертом параметре
+
Соответственно, чтобы заспавнить что-нибудь на другой карте, достаточно указать game_vertex_id в четвертом параметре.
 
Например:
 
Например:
<pre>db.actor:game_vertex_id()</pre>
+
 
 +
<lua>
 +
db.actor:game_vertex_id()
 +
</lua>
  
 
Итак, чтобы, например, заспавнить болт под ногами актора, пишем:  
 
Итак, чтобы, например, заспавнить болт под ногами актора, пишем:  
<pre>alife():create("bolt", db.actor():position(), 1, db.actor:game_vertex_id())</pre>
 
  
Почему 1, а не level_vertex_id? Проверено - разницы особой нет, какой level_vertex_id, хотя в некоторых случаях надо прописывать валидный вертекс, а то предмет может просто заспавнится не там, где планировалось... Но по большей части все проходит нормально и с единицей.
+
<lua>
(Игнорирование level_vertex_id может приводить к проваливанию произведенных предметов/персонажей под землю.)
+
alife():create("bolt", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id())
 +
</lua>
 +
 
 +
Почему 1, а не level_vertex_id? Проверено - разницы особой нет, какой level_vertex_id, хотя в некоторых случаях надо прописывать валидный вертекс, а то предмет может просто заспавнится не там, где планировалось... Но по большей части все проходит нормально и с единицей (игнорирование level_vertex_id может приводить к проваливанию произведенных предметов/персонажей под землю).
 
А вот game_vertex решает все - он указывает на каком уровне спавнить предмет, поэтому его надо указывать. Теоретически можно просто найти для каждого уровня по одному game_vertex'у и использовать их в скриптах.
 
А вот game_vertex решает все - он указывает на каком уровне спавнить предмет, поэтому его надо указывать. Теоретически можно просто найти для каждого уровня по одному game_vertex'у и использовать их в скриптах.
На самом деле game_vertex показывает какой фрагмент карты используется (вся карта разбита на кусочки имеющие сквозную нумерацию по всем уровням и game_vertex выбирает нужный) соответсвенно неправильное использование черевато....
+
На самом деле game_vertex показывает какой фрагмент карты используется (вся карта разбита на кусочки, имеющие сквозную нумерацию по всем уровням, и game_vertex выбирает нужный) соответственно неправильное использование черевато....
  
Кроме того - есть еще один параметр - ID объекта, если указать ID NPC или актора - то предмет заспавнится у него в инвентаре.
+
Кроме того, есть еще один параметр - ID объекта, если указать ID NPC или актора - то предмет заспавнится у него в инвентаре.
  
 
Пример (спавним артефакт Медуза в инвентаре у актора):
 
Пример (спавним артефакт Медуза в инвентаре у актора):
<pre>alife():create("af_medusa", db.actor():position(), 1, db.actor:game_vertex_id(), db.actor:id())</pre>
+
 
 +
<lua>
 +
alife():create("af_medusa", db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())
 +
</lua>
  
 
Функция спавна возвращает серверный объект, то есть ни NPC, ни монстра ни что-либо еще.
 
Функция спавна возвращает серверный объект, то есть ни NPC, ни монстра ни что-либо еще.
  
<span style="color: gray">Серверный обьект позволяет свеже созданного NPC или тайник затарить разными рулезами/артефактами. Например, вот так создадим перед входом к Сидоровичу долговца и засунем в него пачку патронов:
+
<span style="color: gray">Серверный обьект позволяет свежесозданного NPC или тайник затарить разными рулезами/артефактами. Например, вот так создадим перед входом к Сидоровичу долговца и засунем в него пачку патронов:
<pre>
+
<lua>
 
local obj
 
local obj
 
local a = vector() -- Задаем тип переменной
 
local a = vector() -- Задаем тип переменной
Строка 53: Строка 67:
 
obj.m_game_vertex_id,
 
obj.m_game_vertex_id,
 
obj.id,
 
obj.id,
20) -- число патронов</pre>
+
20) -- число патронов
 +
</lua>
  
 
Кстати, create_ammo - практически тоже самое, что и create, разница в том, что create_ammo предназначена специально для спавна патронов и позволяет создавать неполные пачки патронов. Возможно есть еще какие-то отличия. Стоит учесть, что сами авторы игры спавнят патроны исключительно через create_ammo. [[Участник:Imp|Imp]] 22:38, 23 июля 2007 (EEST)</span>
 
Кстати, create_ammo - практически тоже самое, что и create, разница в том, что create_ammo предназначена специально для спавна патронов и позволяет создавать неполные пачки патронов. Возможно есть еще какие-то отличия. Стоит учесть, что сами авторы игры спавнят патроны исключительно через create_ammo. [[Участник:Imp|Imp]] 22:38, 23 июля 2007 (EEST)</span>
  
 
Просто минимальный набор - координаты, ID, секция,а из него (серверного объекта) обычно нужен только ID, так как по ID можно получить этот самый серверный объект:
 
Просто минимальный набор - координаты, ID, секция,а из него (серверного объекта) обычно нужен только ID, так как по ID можно получить этот самый серверный объект:
<pre>(alife():object(id))</pre>
+
 
 +
<lua>
 +
(alife():object(id))
 +
</lua>
  
 
Его можно использовать, чтобы поставить метку, например, но я его лично использую для других целей - спавн сложных объектов, конкретно – NPC.
 
Его можно использовать, чтобы поставить метку, например, но я его лично использую для других целей - спавн сложных объектов, конкретно – NPC.
Строка 64: Строка 82:
 
Например надо решить следующую задачу - надо создать наемника, сменить ему группировку и изменить его инвентарь, ну и в нагрузку - сделать другом для игрока.
 
Например надо решить следующую задачу - надо создать наемника, сменить ему группировку и изменить его инвентарь, ну и в нагрузку - сделать другом для игрока.
  
В определенный момент заспавненый объект переходит онлайн, в этот момент вызывается callback - net_spawn.
+
В определенный момент заспавненный объект переходит онлайн, в этот момент вызывается callback - net_spawn.
  
 
Что мы делаем? Сверяем ID онлайн объекта с сохраненным ID!
 
Что мы делаем? Сверяем ID онлайн объекта с сохраненным ID!
  
 
Если они совпадают, например так:
 
Если они совпадают, например так:
<pre>if obj:id()==saved_id then ...</pre>
+
 
 +
<lua>
 +
if obj:id()==saved_id then ...
 +
</lua>
  
 
Важно то, что у серверного объекта ID - это параметр, а у онлайнового объекта ID получается с помощью функции. Это важно, а то можно прогореть.
 
Важно то, что у серверного объекта ID - это параметр, а у онлайнового объекта ID получается с помощью функции. Это важно, а то можно прогореть.
Строка 79: Строка 100:
 
Зачем такие сложности? Просто в оффлайне NPC как бы не существует, есть только косвенное упоминание о нем, и, плюс, все эти функции работают именно с объектом типа "NPC", а не с серверными объектами.
 
Зачем такие сложности? Просто в оффлайне NPC как бы не существует, есть только косвенное упоминание о нем, и, плюс, все эти функции работают именно с объектом типа "NPC", а не с серверными объектами.
  
<pre>
 
 
Более простая функция спауна в одну строчку:
 
Более простая функция спауна в одну строчку:
Пример:
 
alife():create("stalker",vector():set(0,0,0),1,135) -- спаунит сталкера на водокачке. (Игра ЧН, на уровне Болото)
 
  
 +
<lua>
 
alife():create(section,position,levelvertex,gamevertex)
 
alife():create(section,position,levelvertex,gamevertex)
section - это секция предмета
+
</lua>
position - Это его позиция (Через vector()) Брать в игре с помощью консольной команды rs_stats 1 или rs_cam_pos 1 (Тока в ЧН)
+
 
levelvertex - Пусть равно 1
+
section -- это секция предмета
gamevertex - Его Game Vertex, определяет уровень где будет спаун.
+
position -- это его позиция (Через vector()) Брать в игре с помощью консольной команды rs_stats 1 или rs_cam_pos 1 (Тока в ЧН)
 +
levelvertex - пусть равно 1
 +
gamevertex - его Game Vertex, определяет уровень где будет спаун.
 
GV можно взять либо скриптом, либо через распакованный all.spawn.
 
GV можно взять либо скриптом, либо через распакованный all.spawn.
</pre>
+
 
 +
Пример:
 +
 
 +
<lua>
 +
alife():create("stalker",vector():set(0,0,0),1,135) -- спаунит сталкера на водокачке (игра ЧН, на уровне Болото).
 +
</lua>
  
 
==Практика (часть 1)==
 
==Практика (часть 1)==
Строка 98: Строка 124:
 
Небольшое отступление:
 
Небольшое отступление:
  
Почему предпочтительнее делать спавн скриптом, а не через тот же xrSpawner? Программа xrSpawner, при всех своих достоинствах, обладает одним недостатком, а именно – она делает спавн через файл all.spawn, что приводит к:
+
Почему предпочтительнее делать спавн скриптом, а не через тот же [http://stalkerin.gameru.net/downloads/stutils/zeed/xrspawner.rar xrSpawner]? Программа xrSpawner, при всех своих достоинствах, обладает одним недостатком, а именно – редактирует спавн через файл all.spawn, что приводит к:
  
 
* Невозможности совместить два мода, такой спавн использующих
 
* Невозможности совместить два мода, такой спавн использующих
 
* Необходимости каждый раз начинать новую игру
 
* Необходимости каждый раз начинать новую игру
  
При спавне через скрипт ситуация иная: в подавляющем большинстве случаев, ранее сохраненные игры будут работать, что не может не радовать :)
+
При спавне через скрипт ситуация иная: в подавляющем большинстве случаев, ранее сохранённые игры будут работать, что не может не радовать :)
  
 
Итак, определимся с квестом.
 
Итак, определимся с квестом.
Строка 113: Строка 139:
 
В консоли введите команду:
 
В консоли введите команду:
  
<pre>rs_stats on или rs_stats 1 </pre>
+
<ini>
 +
rs_stats on или rs_stats 1
 +
</ini>
  
 
Тем самым мы включаем вывод информации на экран.  
 
Тем самым мы включаем вывод информации на экран.  
 
Далее вводим еще одну команду:
 
Далее вводим еще одну команду:
  
<pre>demo_record 1 </pre>
+
<ini>
 +
demo_record 1
 +
</ini>
  
 
И «летим» на фабрику. Нам нужно выбрать место для спавна объектов и данный режим как нельзя лучше подходит для реализации задуманного. Помещаем камеру в точке предполагаемого спавна и записываем координаты - у меня получились 115, -6, -16.  
 
И «летим» на фабрику. Нам нужно выбрать место для спавна объектов и данный режим как нельзя лучше подходит для реализации задуманного. Помещаем камеру в точке предполагаемого спавна и записываем координаты - у меня получились 115, -6, -16.  
Строка 125: Строка 155:
  
 
<span style="color: gray">Другой способ получения тех же сведений - прийти в нужное место и запустить там скрипт, который выдаст все нужные координаты. Я пользуюсь следующим скриптом (вызываю общеизвестным способом, через main_menu):
 
<span style="color: gray">Другой способ получения тех же сведений - прийти в нужное место и запустить там скрипт, который выдаст все нужные координаты. Я пользуюсь следующим скриптом (вызываю общеизвестным способом, через main_menu):
<pre>
+
 
 +
<lua>
 
function main_menu:main_cheat_f3()
 
function main_menu:main_cheat_f3()
 
-- Выдадим сообщение о нашем местоположении
 
-- Выдадим сообщение о нашем местоположении
Строка 140: Строка 171:
 
news_manager.send_tip(db.actor, text, nil, nil, 30000)
 
news_manager.send_tip(db.actor, text, nil, nil, 30000)
 
end
 
end
</pre>
+
</lua>
В результате не нужно эксперементировать мы сразу получаем все, в том числе и level_vertex и  game_vertex. [[Участник:Imp|Imp]] 22:38, 23 июля 2007 (EEST)</span>
+
 
 +
В результате не нужно экспериментировать, мы сразу получаем все, в том числе и level_vertex и  game_vertex. [[Участник:Imp|Imp]] 22:38, 23 июля 2007 (EEST)</span>
 +
 
 +
<span style="color: gray">
 +
Или же используем готовые скрипты с автообновлением позиционирования:
 +
<br>
 +
[http://stalkerin.gameru.net/downloads/stutils/LVIDGVID_script.7z LVID GVID Script SoC]
 +
<br>
 +
[http://stalkerin.gameru.net/downloads/stutils/script_position_cs.rar LVID GVID Script CS]
 +
<br>
 +
[http://stalkerin.gameru.net/downloads/stutils/LVID_GVID_COP.rar LVID GVID Script CoP]
 +
<br>
 +
[http://stalkerin.gameru.net/downloads/stutils/lv_gv_id_3120.rar LVID GVID Script 3120]
 +
<br>
 +
[http://stalkerin.gameru.net/downloads/stutils/LVIDGVID_script_amk.7z LVID GVID Script AMK]
 +
<br>
 +
[[Участник:RedPython|RedPython]] 16:09, 10 октября 2010 (UTC)
 +
</span>
  
 
Выходим из игры, идем в папку с установленной игрой и создаем каталог gamedata (предполагается, что «лепим» свой «мод» на «чистую» игру,  без установленных модов, и имеем распакованные ресурсы игры в папке, скажем, gamedata source).
 
Выходим из игры, идем в папку с установленной игрой и создаем каталог gamedata (предполагается, что «лепим» свой «мод» на «чистую» игру,  без установленных модов, и имеем распакованные ресурсы игры в папке, скажем, gamedata source).
Строка 147: Строка 195:
 
В папке gamedata создаем папку config, а в ней - папку creatures. Скопируем из оригинальной папки файл m_zombie.ltx и откроем его на редактирование.
 
В папке gamedata создаем папку config, а в ней - папку creatures. Скопируем из оригинальной папки файл m_zombie.ltx и откроем его на редактирование.
  
В файлах игры присутствуют 5 моделей гражданских зомби:
+
В файлах игры присутствуют 5 моделей гражданских зомби: файлы zombi_1.ogf, zombi_1_ghost.ogf, zombi_2.ogf, zombi_trup.ogf, zombi_trup_2.ogf.
+
<pre>файлы zombi_1.ogf, zombi_1_ghost.ogf, zombi_2.ogf, zombi_trup.ogf, zombi_trup_2.ogf</pre>
+
  
 
Вернем в игру их всех :)
 
Вернем в игру их всех :)
Строка 155: Строка 201:
 
Уже имеются секции:
 
Уже имеются секции:
  
<pre>[zombie_weak]:m_zombie_e, [zombie_normal]:m_zombie_e, [zombie_strong]:m_zombie_e и [zombie_immortal]:zombie_strong. </pre>
+
<pre>
 +
[zombie_weak]:m_zombie_e, [zombie_normal]:m_zombie_e, [zombie_strong]:m_zombie_e и [zombie_immortal]:zombie_strong.
 +
</pre>
  
 
Два последних типа используют одну и ту же модель zombi_trup.ogf, хм... непорядок, исправляем.  
 
Два последних типа используют одну и ту же модель zombi_trup.ogf, хм... непорядок, исправляем.  
 
Последняя секция выглядит теперь так:
 
Последняя секция выглядит теперь так:
  
<pre>[zombie_immortal]:zombie_strong
+
<ini>
 +
[zombie_immortal]:zombie_strong
 
$spawn = "monsters\zombies\zombie_immortal"
 
$spawn = "monsters\zombies\zombie_immortal"
 
visual = monsters\zombi\zombi_trup_2
 
visual = monsters\zombi\zombi_trup_2
panic_threshold = 0.05 </pre>
+
panic_threshold = 0.05
 +
</ini>
  
 
Добавим пятую модель.  
 
Добавим пятую модель.  
  
 
Для этого в конце файла создадим секцию:
 
Для этого в конце файла создадим секцию:
<pre>[zombie_ghost]:zombie_strong</pre>
+
<ini>
 +
[zombie_ghost]:zombie_strong
 +
</ini>
  
 
Это означает, что наш пятый зомби наследует все параметры zombie_strong, мы добавим лишь визуальное представление.
 
Это означает, что наш пятый зомби наследует все параметры zombie_strong, мы добавим лишь визуальное представление.
Строка 174: Строка 226:
 
Пишем дальше:
 
Пишем дальше:
  
<pre>$spawn = "monsters\zombies\zombie_ghost"
+
<ini>
visual = monsters\zombi\zombi_1_ghost</pre>
+
$spawn = "monsters\zombies\zombie_ghost"
 +
visual = monsters\zombi\zombi_1_ghost
 +
</ini>
  
 
Все. Сохраняем изменения и закрываем файл.
 
Все. Сохраняем изменения и закрываем файл.
Строка 187: Строка 241:
 
Итак, открываем наш пустой файл на редактирование, первой строкой объявляем переменную, в которой хранятся наши зомби:
 
Итак, открываем наш пустой файл на редактирование, первой строкой объявляем переменную, в которой хранятся наши зомби:
  
<pre>local zombie_types = {"zombie_weak", "zombie_normal", "zombie_strong", "zombie_immortal", "zombie_ghost"}</pre>
+
<lua>
 +
local zombie_types = {"zombie_weak", "zombie_normal", "zombie_strong", "zombie_immortal", "zombie_ghost"}
 +
</lua>
  
 
Далее пишем функцию:
 
Далее пишем функцию:
  
<pre>function spawn_zombies( position, total )
+
<lua>
local zombie_index -- тип зомби из массива zombie_types
+
function spawn_zombies( position, total )
local new_pos, x_offset, z_offset -- объявляем переменные
+
local zombie_index                             -- тип зомби из массива zombie_types
for zombie_index=1, total do -- крутим цикл столько раз, сколько задает переменная total
+
local new_pos, x_offset, z_offset               -- объявляем переменные
x_offset = math.random(5) -- случайное (рандомное)  x от 1 до 5
+
for zombie_index=1, total do                   -- крутим цикл столько раз, сколько задает переменная total
z_offset = math.random(5) -- случайное (рандомное)  z от 1 до 5
+
x_offset = math.random(5)               -- случайное (рандомное)  x от 1 до 5
new_pos = position -- передаем координаты в функцию
+
z_offset = math.random(5)               -- случайное (рандомное)  z от 1 до 5
new_pos.x = new_pos.x + x_offset -- прибавляем к указанной нами координате x полученное выше рандомное x
+
new_pos = position                     -- передаем координаты в функцию
new_pos.z = new_pos.z + z_offset -- прибавляем к указанной нами координате z полученное выше рандомное z
+
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)] привязанного к нашим координатам
 
-- Ниже, собственно и вызывается функция спавна случайного типа зомби zombie_types[math.random(5)] привязанного к нашим координатам
 
alife():create(zombie_types[math.random(5)],new_pos,db.actor:level_vertex_id(),db.actor:game_vertex_id())
 
alife():create(zombie_types[math.random(5)],new_pos,db.actor:level_vertex_id(),db.actor:game_vertex_id())
 
end
 
end
end  
+
end
</pre>
+
 
 +
</lua>
  
 
И последнее:
 
И последнее:
  
<pre>function zombie_story_1( actor, npc )
+
<lua>
-- десять зомби на фабрике (Кордон)
+
function zombie_story_1( actor, npc )
local spawn_point = vector():set( 115, -6, -16 ) -- здесь указываем координаты,  
+
-- десять зомби на фабрике (Кордон)
-- выбранные нами для спавна, когда «летали» камерой :)
+
local spawn_point = vector():set( 115, -6, -16 )   -- здесь указываем координаты,
spawn_zombies( spawn_point, 10 ) -- собственно вызов предыдущей функции  
+
                                                            -- выбранные нами для спавна, когда «летали» камерой :)
-- с передачей ей координат и количества объектов
+
spawn_zombies( spawn_point, 10 )                   -- собственно вызов предыдущей функции
end</pre>
+
                                                            -- с передачей ей координат и количества объектов
 +
end
 +
</lua>
  
 
Все. Сохраняем и закрываем файл.
 
Все. Сохраняем и закрываем файл.
Строка 220: Строка 280:
 
Продолжаем разговор :)  
 
Продолжаем разговор :)  
  
Для того, чтобы игра не вылетала после того, как мы добавили новый тип монстров, их нужно добавить в файл xr_statistic.script. Итак, скопируем этот файл из папки игры scripts в нашу папку к файлу esc_zombie.script и откроем на редактирование.  
+
Для того, чтобы игра не вылетала после того, как мы добавили новый тип монстров, их нужно добавить в файл xr_statistic.script. Итак, скопируем этот файл из папки ресурсов игры scripts в нашу папку к файлу esc_zombie.script и откроем на редактирование.  
  
 
Добавим в local killCountProps  к монстрам строчку:
 
Добавим в local killCountProps  к монстрам строчку:
<pre> zombie_weak = 1, zombie_normal = 2, zombie_strong = 3</pre>
+
<lua>
 +
zombie_weak = 1, zombie_normal = 2, zombie_strong = 3, zombie_immortal = 4, zombie_ghost = 5
 +
</lua>
 +
 
 +
В local sect_alias после строки:
 +
 
 +
<lua>
 +
zombied_novice = 1, zombied_experienced = 2, zombied_veteran = 3, zombied_master = 4,
 +
</lua>
 +
 
 +
Допишем эти строки:
 +
 
 +
<lua>
 +
zombie_weak = "zombie_weak", zombie_normal  = "zombie_normal", zombie_strong = "zombie_strong",
 +
zombie_immortal = "zombie_immortal", zombie_ghost = "zombie_ghost",
 +
</lua>
  
В local sect_alias строчку:
 
<pre> zombie_weak = "zombie_weak", zombie_normal  = "zombie_normal", zombie_strong = "zombie_strong"</pre>
 
 
А ниже в monster_classes строчку:
 
А ниже в monster_classes строчку:
<pre> [clsid.zombie_s     ] = "zombie"</pre>
+
 
 +
<lua>
 +
[clsid.zombie_s] = "zombie"
 +
</lua>
 +
 
 
В функцию getNpcType(npc) добавляем конструкцию:
 
В функцию getNpcType(npc) добавляем конструкцию:
<pre> elseif npc:character_community() == "zombie" then
+
 
community = "zombie"</pre>
+
<lua>
 +
elseif npc:character_community() == "zombie" then
 +
community = "zombie"
 +
</lua>
  
 
Сохраняем изменения и закрываем файл.
 
Сохраняем изменения и закрываем файл.
Строка 237: Строка 317:
 
Все будет работать на ура, пока мы не попробуем обыскать убитого зомби. Как только мы это сделаем, игра вылетит с примерно такой ошибкой.
 
Все будет работать на ура, пока мы не попробуем обыскать убитого зомби. Как только мы это сделаем, игра вылетит с примерно такой ошибкой.
  
<pre>Expression    : fatal error
+
<ini>
 +
Expression    : fatal error
 
Function      : CInifile::r_string
 
Function      : CInifile::r_string
 
File          : D:\xray-svn\xrCore\Xr_ini.cpp
 
File          : D:\xray-svn\xrCore\Xr_ini.cpp
 
Line          : 351
 
Line          : 351
 
Description  : <no expression>
 
Description  : <no expression>
Arguments    : Can't find variable icon in [zombie_weak]</pre>
+
Arguments    : Can't find variable icon in [zombie_weak]
 +
</ini>
  
Все верно – игра не знает какую иконку нам показывать для зомби. Иконки монстров хранятся в файле ui_npc_monster.dds. Здесь есть два варианта:
+
Все верно – игра не знает, какую иконку нам показывать для зомби. Иконки монстров хранятся в файле ui_npc_monster.dds. Здесь есть два варианта:
  
 
* Если дружите с Фотошопом, отредактировать этот файл (нарисовать, добавить иконки);
 
* Если дружите с Фотошопом, отредактировать этот файл (нарисовать, добавить иконки);
Строка 250: Строка 332:
  
 
Вернемся к файлу m_zombie.ltx и в секцию [m_zombie_e]:monster_base впишем параметр
 
Вернемся к файлу m_zombie.ltx и в секцию [m_zombie_e]:monster_base впишем параметр
<pre> icon = ui_npc_monster_kontroler</pre>
+
<ini>
 +
icon = ui_npc_monster_kontroler
 +
</ini>
  
 
Все. Вылетов не будет.
 
Все. Вылетов не будет.
  
3. Тема данной статьи не предусматривает подробного описания того, как сделать новый диалог. В начале статьи я упомянул источник, где можно найти исчерпывающую информацию по созданию диалогов, могу также привести в пример [[Создание диалогов (BAC9-FLCL)|статью по созданию диалогов]] от BAC9-FLCL.
+
3. Тема данной статьи не предусматривает подробного описания того, как сделать новый диалог. В начале статьи я упомянул источник, где можно найти исчерпывающую информацию по созданию диалогов, могу также привести в пример [[Создание диалогов|статью по созданию диалогов]] от BAC9-FLCL.
  
 
Нам нужно просто проверить работоспособность скриптового спавна, поэтому я приведу просто собственно сам измененный диалог из файла dialogs_escape.xml:
 
Нам нужно просто проверить работоспособность скриптового спавна, поэтому я приведу просто собственно сам измененный диалог из файла dialogs_escape.xml:
  
<pre>   <dialog id="escape_trader_talk_info">
+
<xml>
 +
<dialog id="escape_trader_talk_info">
 +
………
 +
………
 
………
 
………
 
             <phrase id="999">
 
             <phrase id="999">
Строка 336: Строка 423:
 
                 <next>9995</next>
 
                 <next>9995</next>
 
             </phrase>
 
             </phrase>
……
+
………
     </dialog></pre>
+
………
 +
………
 +
     </dialog>
 +
</xml>
  
 
И также связанный с ним файл stable_dialogs_escape.xml.
 
И также связанный с ним файл stable_dialogs_escape.xml.
 
В самом начале файла пишем следующее:
 
В самом начале файла пишем следующее:
  
<pre> <string id="escape_trader_talk_info_7770">
+
<xml>
 +
<string id="escape_trader_talk_info_7770">
 
<text>Происшествий никаких не было?</text>
 
<text>Происшествий никаких не было?</text>
 
</string>
 
</string>
Строка 366: Строка 457:
 
<string id="esc_bridge_soldiers_start_11">
 
<string id="esc_bridge_soldiers_start_11">
 
<text>Здесь проход воспрещён, сталкер.</text>
 
<text>Здесь проход воспрещён, сталкер.</text>
</string></pre>
+
</string>
 +
</xml>
  
Все. Можно запускать игру, идти на Кордон, после разговороа с Сидоровичем, в зависимости  
+
Все. Можно запускать игру, идти на Кордон, после разговора с Сидоровичем, в зависимости  
 
от выбранного Меченным решения, бежим на фабрику и … смотрим сами :)
 
от выбранного Меченным решения, бежим на фабрику и … смотрим сами :)
  
 
  [http://www.sendspace.com/file/2kx7uf Готовые файлы примера](перезалито)
 
  [http://www.sendspace.com/file/2kx7uf Готовые файлы примера](перезалито)
 
  [http://slil.ru/24600587 Spawn Lib]
 
  [http://slil.ru/24600587 Spawn Lib]
 +
 
Домашнее задание - вернуть в игру 6-ой тип гражданского зомби :)
 
Домашнее задание - вернуть в игру 6-ой тип гражданского зомби :)
  
Строка 379: Строка 472:
 
==Практика (часть 2)==
 
==Практика (часть 2)==
  
'''4.''' Сегодня мы закончим с зомби в полном объеме – добавим их описания в энциклопедию,  добавим иконки, и разберемся с «домашним заданием»:) Думаю, что внимательно изучив эту статью, вы ''сами'' сможете через скриптовые функции восстановить любого персонажа, не вошедшего в финальный релиз игры. Если у кого хватит времени и желания, те могут даже написать что-то типа «Ночи живых мертвецов» :) - серию квестов, связанную своим собственным сюжетом.  
+
'''4.''' Сегодня мы закончим с зомби в полном объеме – добавим их описания в энциклопедию,  добавим иконки, и разберемся с «домашним заданием»:) Думаю, что внимательно изучив эту статью, вы ''сами'' сможете через скриптовые функции восстановить любого персонажа, не вошедшего в финальный релиз игры. Если у кого хватит времени и желания, те могут даже написать что-то типа «Ночи живых мертвецов» :) - серию квестов, связанную своим собственным сюжетом.  
 
Итак, «домашнее задание» - добавляем в игру шестого зомби.
 
Итак, «домашнее задание» - добавляем в игру шестого зомби.
  
 
* Создаем в папке gamedata папку meshes, в ней папку monsters, а там – папку zombi. В папке meshes хранятся модели персонажей, объектов, окружения, присутствующих в игре. Как я уже говорил выше, в игре представлено 5 моделей гражданских зомби, а вот текстур – 6. Также немного смутила нумерация моделей – 1, 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). Так как данная статья рассчитана не только на «продвинутых юзеров», но и на «обычных чайников», которые, тем не менее, хотят «что-нить замутить» и при этом не сильно «парить моск», я не буду рассказывать здесь про адресацию, двухбайтовую запись и т.д., я просто наглядно покажу, где что поправить :)
+
* Скопируем из папки с оригинальными файлами игры в созданную нами папку файл zombi файл zombi_2.ogf и переименуем его в zombi_3.ogf. Откроем файл нашей новой модели любым HexEditor’ом, я использую BiEd (Binary Editor 1.00). Так как данная статья рассчитана не только на «продвинутых юзеров», но и на «обычных чайников», которые, тем не менее, хотят «что-нить замутить» и при этом не сильно «парить моск», я не буду рассказывать здесь про адресацию, двухбайтовую запись и т.д., я просто наглядно покажу, где что поправить :)
  
 
[http://www.golushkov.pp.net.ua/photo/11-0-717-3 Файл модели '''до''' редактирования]
 
[http://www.golushkov.pp.net.ua/photo/11-0-717-3 Файл модели '''до''' редактирования]
Строка 394: Строка 487:
 
'''5.''' Теперь пропишем нашего нового зомби во все файлы, которые мы создали ранее. В файл m_zombie.ltx в самый конец добавляем секцию:
 
'''5.''' Теперь пропишем нашего нового зомби во все файлы, которые мы создали ранее. В файл m_zombie.ltx в самый конец добавляем секцию:
  
<pre>[zombie_old]:zombie_normal
+
<ini>
 +
[zombie_old]:zombie_normal
 
$spawn = "monsters\zombies\zombie_old"
 
$spawn = "monsters\zombies\zombie_old"
visual = monsters\zombi\zombi_3</pre>
+
visual = monsters\zombi\zombi_3
 +
</ini>
  
 
в файле esc_zombie.script изменяем массив в первой строке:
 
в файле esc_zombie.script изменяем массив в первой строке:
  
<pre>local zombie_types = {"zombie_weak", "zombie_normal", "zombie_strong", "zombie_immortal",  
+
<lua>
"zombie_old", "zombie_ghost"}</pre>
+
local zombie_types = {"zombie_weak", "zombie_normal", "zombie_strong", "zombie_immortal",  
 +
"zombie_old", "zombie_ghost"}
 +
</lua>
  
 
В функции spawn_zombies изменяем строку спавна:
 
В функции spawn_zombies изменяем строку спавна:
  
<pre> alife():create(zombie_types[math.random(6)],new_pos,
+
<lua>
db.actor:level_vertex_id(),db.actor:game_vertex_id())</pre>
+
alife():create(zombie_types[math.random(6)],new_pos,
в функции zombie_story_1 меняем число объектов на кратное 6-ти (необязательно):
+
db.actor:level_vertex_id(),db.actor:game_vertex_id())
 +
</lua>
  
<pre> spawn_zombies( spawn_point, 12 )</pre>
+
В функции zombie_story_1 меняем число объектов на кратное 6-ти (необязательно):
 +
 
 +
<lua>
 +
spawn_zombies( spawn_point, 12 )
 +
</lua>
  
 
Всё. Сохраняем и закрываем.
 
Всё. Сохраняем и закрываем.
Строка 415: Строка 517:
 
'''6.''' Копируем в папку gamedata\config\gameplay\  файл encyclopedia_mutants.xml, добавляем описание зомби в энциклопедию:
 
'''6.''' Копируем в папку gamedata\config\gameplay\  файл encyclopedia_mutants.xml, добавляем описание зомби в энциклопедию:
  
<pre>   <!-------------------------------- Zombieg ----------------------------->
+
<xml>
 +
    <!-------------------------------- Zombieg ----------------------------->
  
 
     <article id="mutant_zombieg_general" name="Zombieg" group="Mutants">
 
     <article id="mutant_zombieg_general" name="Zombieg" group="Mutants">
Строка 421: Строка 524:
 
         <text>enc_mutant_zombieg_general</text>
 
         <text>enc_mutant_zombieg_general</text>
 
     </article>
 
     </article>
</pre>
+
</xml>
  
 
И в связанный с ним файл string_table_enc_mutants.xml в папке gamedata\config\text\rus\ добавляем:
 
И в связанный с ним файл string_table_enc_mutants.xml в папке gamedata\config\text\rus\ добавляем:
  
<pre> <string id="Zombie">
+
<xml>
 +
<string id="Zombie">
 
<text>Зомби, гражданский</text>
 
<text>Зомби, гражданский</text>
 
</string>
 
</string>
Строка 448: Строка 552:
 
стараются обходить эти неуклюжие опустошённые оболочки.</text>
 
стараются обходить эти неуклюжие опустошённые оболочки.</text>
 
</string>
 
</string>
</pre>
+
</xml>
  
 
Копируем сюда же файл stable_statistic_caption.xml и изменяем в нем 3 строчки:
 
Копируем сюда же файл stable_statistic_caption.xml и изменяем в нем 3 строчки:
  
<pre> <string id="zombie_normal">
+
<xml>
 +
<string id="zombie_normal">
 
<text>зомбированный, гражданский</text>
 
<text>зомбированный, гражданский</text>
 
</string>
 
</string>
Строка 461: Строка 566:
 
<text>зомби, гражданский</text>
 
<text>зомби, гражданский</text>
 
</string>
 
</string>
</pre>
+
</xml>
  
 
Сохраняем и закрываем.
 
Сохраняем и закрываем.
Строка 469: Строка 574:
 
Вкратце, что описывает файл ui_npc_monster.xml: в нем задаются координаты иконок, расположенных в файле ui_npc_monster.dds, применительно к каждому типу монстров в игре.
 
Вкратце, что описывает файл ui_npc_monster.xml: в нем задаются координаты иконок, расположенных в файле ui_npc_monster.dds, применительно к каждому типу монстров в игре.
  
Заключительный штрих. Откройте файл m_zombie.ltx и в первой секции замените строку  
+
Заключительный штрих. Откройте файл m_zombie.ltx и в первой секции замените строку
<pre>icon = ui_npc_monster_kontroler</pre>
+
 
На:
+
<ini>
<pre>icon = ui_npc_monster_zombie</pre>
+
icon = ui_npc_monster_kontroler
 +
</ini>
 +
 
 +
на:
 +
 
 +
<ini>
 +
icon = ui_npc_monster_zombie
 +
</ini>
 +
 
 
В секцию [zombie_ghost] добавьте строку:
 
В секцию [zombie_ghost] добавьте строку:
<pre>icon = ui_npc_monster_zombieg</pre>
+
 
 +
<ini>
 +
icon = ui_npc_monster_zombieg
 +
</ini>
  
 
Сохраняйте изменения. Всё.  
 
Сохраняйте изменения. Всё.  
Строка 491: Строка 607:
 
* gamedata\config\creatures\spawn_sections.ltx
 
* gamedata\config\creatures\spawn_sections.ltx
  
Начнем с ''character_desc_escape.xml''. Описание что значит каждая строка писать не буду, так как все до меня уже сделано.
+
Начнем с ''character_desc_escape.xml''. Описание, что значит каждая строка, писать не буду, так как все до меня уже сделано.
  
 
Тут создадим нового персонажа:
 
Тут создадим нового персонажа:
  
<pre><specific_character id="esc_dark_stalker_1" team_default = "1">
+
<xml>
 +
<specific_character id="esc_dark_stalker_1" team_default = "1">
 
<name>GENERATE_NAME_bandit</name>
 
<name>GENERATE_NAME_bandit</name>
 
<icon>ui_npc_dark_1</icon>
 
<icon>ui_npc_dark_1</icon>
Строка 525: Строка 642:
 
<start_dialog>dm_hello_dialog</start_dialog>
 
<start_dialog>dm_hello_dialog</start_dialog>
 
<actor_dialog>dm_cool_info_dialog</actor_dialog>
 
<actor_dialog>dm_cool_info_dialog</actor_dialog>
</specific_character></pre>
+
</specific_character>
 +
</xml>
  
Впишем наш код после какого-нибудь </specific_character>
+
Впишем наш код после какого-нибудь </specific_character>.
  
 
Теперь идем в ''npc_profile.xml'' и туда вгоняем:
 
Теперь идем в ''npc_profile.xml'' и туда вгоняем:
  
<pre><character id="esc_dark_stalker_1">
+
<xml>
 +
<character id="esc_dark_stalker_1">
 
<class>esc_dark_stalker_01</class>
 
<class>esc_dark_stalker_01</class>
 
<specific_character>esc_dark_stalker_1</specific_character>
 
<specific_character>esc_dark_stalker_1</specific_character>
</character></pre>
+
</character>
 +
</xml>
  
 
Теперь надо заняться ''spawn_sections.ltx''. Скрипт будет «брать NPC» как раз из этого файла. Пишем туда:
 
Теперь надо заняться ''spawn_sections.ltx''. Скрипт будет «брать NPC» как раз из этого файла. Пишем туда:
Строка 545: Строка 665:
  
 
Теперь берем любой скрипт спавна NPC, вгоняем туда имя секции из spawn_sections.ltx и ву-а-ля.
 
Теперь берем любой скрипт спавна NPC, вгоняем туда имя секции из spawn_sections.ltx и ву-а-ля.
 +
 +
Ну, к примеру, создаем свой файл со скриптом спавна, назовем его - spaw_new_npc.script
 +
и запишем туда вот такую функцию (взял из статьи):
 +
 +
<lua>
 +
function spaw_n_n() --тут задается сама функция, которая в последующем будет активирована, к примеру с помощью диалога
 +
local a = vector() -- db.actor:position() -- тип переменной, которая будет задействована в спавне, вместо кооржинат будет писаться a
 +
a.x = -220.21
 +
a.y = -19.93
 +
a.z = -158.98
 +
alife():create("wpn_ak47",a,33447,47,65535) -- здесь сам спавн объекта, a - тип переменной которую мы указали выше
 +
end
 +
</lua>
 +
 +
Вот и весь скрипт! Теперь надо его активировать, ну к примеру через диалог, после диалога вставляем:
 +
 +
<xml>
 +
<action>spaw_new_npc.spaw_n_n</action>
 +
</xml>
 +
 +
распишу, что значит:
 +
 +
<xml>
 +
<action>названиескрипта.функциякоторуюнеобходимоактивировать</action>
 +
</xml>
 +
 +
<span style="color: gray">Например:
 +
 +
<xml>
 +
<game_dialogs>
 +
………
 +
………
 +
………
 +
    <dialog id="lokationname_testdialog_start">
 +
        <phrase_list>
 +
            <phrase id="1">
 +
                <text>locationname_testdialog_start_1</text>    <!-- ссылка на файл с текстом фразы -->
 +
                <action>test_script.testfunction</action>    <!-- ссылка на функцию testfunction скрипта test_script -->
 +
            </phrase>
 +
        </phrase_list>
 +
    </dialog>
 +
………
 +
………
 +
………
 +
</game_dialogs>
 +
</xml>
 +
[[Участник:RedPython|RedPython]] 16:09, 10 октября 2010 (UTC)
 +
</span>
  
 
Тока забыли что данному НПС будет присвоенная дефолтовая логика (т.е. тупо хожу куда сам не знаю) забыли упомянуть про (ххх - лока):<br>
 
Тока забыли что данному НПС будет присвоенная дефолтовая логика (т.е. тупо хожу куда сам не знаю) забыли упомянуть про (ххх - лока):<br>
Строка 561: Строка 729:
  
 
Статья исправлена:
 
Статья исправлена:
* [[Участник:Rekongstor]]
+
* [[Участник:Rekongstor|Rekongstor]]
 +
 
 +
В статью добавили:
 +
* [[Участник:Byurrer]]
  
 
[[Категория:Скрипты]]
 
[[Категория:Скрипты]]

Текущая версия на 21:39, 29 января 2017

Теория

В скриптах есть одна единственная функция, отвечающая за спавн объектов:

 
alife():create(section,position,levelvertex,gamevertex)
 

Строго говоря, их две: create и create_ammo но различия между ними не существенны. Imp 22:45, 23 июля 2007 (EEST)

Первый параметр - секция в конфигурациях, описывающая объект, например "bolt","medkit" - это простые секции, простых объектов а есть объекты, которые переходят в онлайн/оффлайн, это неписи, монстры и так далее, например mil_killer_respawn_2 - спавнится снайпер группировки киллеров.

С позицией, думаю объяснять не надо, только существует нюанс - высота это Y, а не Z.

Направления в пространстве

Задать позицию можно такой конструкцией: vector():set(x,y,z), где x, y и z - координаты точки на уровне, где спавним объект.

Вертекс в игре Сталкер не тоже самое, что вертекс в трехмерной графике. Vertex - определенная зона малого размера за которой закреплен индивидуальный номер в пределах уровня или целой игры. level_vertex определяет зону на уровне, по которой можно пройти NPC. game_vertex определяют зоны для свободного хождения, т.е. под руководством ИИ, а также между уровнями. Вертексы имеют одно из первостепенных значений. Точность положения указывается уже через точку спавна. Например, можно получить вертекс ближайший к актору - db.actor:level_vertex_id()

game_vertex_id нужен для того, чтобы указать на какой карте спавнить объект.

Соответственно, чтобы заспавнить что-нибудь на другой карте, достаточно указать game_vertex_id в четвертом параметре. Например:

 
db.actor:game_vertex_id()
 

Итак, чтобы, например, заспавнить болт под ногами актора, пишем:

 
alife():create("bolt", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id())
 

Почему 1, а не level_vertex_id? Проверено - разницы особой нет, какой level_vertex_id, хотя в некоторых случаях надо прописывать валидный вертекс, а то предмет может просто заспавнится не там, где планировалось... Но по большей части все проходит нормально и с единицей (игнорирование level_vertex_id может приводить к проваливанию произведенных предметов/персонажей под землю). А вот game_vertex решает все - он указывает на каком уровне спавнить предмет, поэтому его надо указывать. Теоретически можно просто найти для каждого уровня по одному game_vertex'у и использовать их в скриптах. На самом деле game_vertex показывает какой фрагмент карты используется (вся карта разбита на кусочки, имеющие сквозную нумерацию по всем уровням, и game_vertex выбирает нужный) соответственно неправильное использование черевато....

Кроме того, есть еще один параметр - ID объекта, если указать ID NPC или актора - то предмет заспавнится у него в инвентаре.

Пример (спавним артефакт Медуза в инвентаре у актора):

 
alife():create("af_medusa", db.actor:position(), 1, db.actor:game_vertex_id(), 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", а не с серверными объектами.

Более простая функция спауна в одну строчку:

 
alife():create(section,position,levelvertex,gamevertex)
 

section -- это секция предмета position -- это его позиция (Через vector()) Брать в игре с помощью консольной команды rs_stats 1 или rs_cam_pos 1 (Тока в ЧН) levelvertex - пусть равно 1 gamevertex - его Game Vertex, определяет уровень где будет спаун. GV можно взять либо скриптом, либо через распакованный all.spawn.

Пример:

 
alife():create("stalker",vector():set(0,0,0),1,135) -- спаунит сталкера на водокачке (игра ЧН, на уровне Болото).
 

Практика (часть 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)

Или же используем готовые скрипты с автообновлением позиционирования:
LVID GVID Script SoC
LVID GVID Script CS
LVID GVID Script CoP
LVID GVID Script 3120
LVID GVID Script AMK
RedPython 16:09, 10 октября 2010 (UTC)

Выходим из игры, идем в папку с установленной игрой и создаем каталог 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 zombie_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, zombie_immortal = 4, zombie_ghost = 5
 

В local sect_alias после строки:

 
zombied_novice = 1, zombied_experienced = 2, zombied_veteran = 3, zombied_master = 4,
 

Допишем эти строки:

 
zombie_weak = "zombie_weak", zombie_normal  = "zombie_normal", zombie_strong = "zombie_strong",
zombie_immortal = "zombie_immortal", zombie_ghost = "zombie_ghost",
 

А ниже в 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 и ву-а-ля.

Ну, к примеру, создаем свой файл со скриптом спавна, назовем его - spaw_new_npc.script и запишем туда вот такую функцию (взял из статьи):

 
function spaw_n_n() --тут задается сама функция, которая в последующем будет активирована, к примеру с помощью диалога
	local a = vector() -- db.actor:position() -- тип переменной, которая будет задействована в спавне, вместо кооржинат будет писаться a 
	a.x = -220.21
	a.y = -19.93
	a.z = -158.98
	alife():create("wpn_ak47",a,33447,47,65535) -- здесь сам спавн объекта, a - тип переменной которую мы указали выше
end
 

Вот и весь скрипт! Теперь надо его активировать, ну к примеру через диалог, после диалога вставляем:

 
<action>spaw_new_npc.spaw_n_n</action>
 

распишу, что значит:

 
<action>названиескрипта.функциякоторуюнеобходимоактивировать</action>
 

Например:

 
<game_dialogs>
………
………
………
    <dialog id="lokationname_testdialog_start">
        <phrase_list>
            <phrase id="1">
                <text>locationname_testdialog_start_1</text>    <!-- ссылка на файл с текстом фразы -->
                <action>test_script.testfunction</action>    <!-- ссылка на функцию testfunction скрипта test_script -->
            </phrase>
        </phrase_list>
    </dialog>
………
………
………
</game_dialogs>
 

RedPython 16:09, 10 октября 2010 (UTC)

Тока забыли что данному НПС будет присвоенная дефолтовая логика (т.е. тупо хожу куда сам не знаю) забыли упомянуть про (ххх - лока):
ххх gulag.script (действия НПС! Где можно: где можно присвоить статус кампера; волкера; и.д.т)
ххх gulag.ltx (дублер логики т.е. дублирует кратко действия НПС)

Авторы

Статья создана:

Статья исправлена:

В статью добавили:

Другие места
LANGUAGE