<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://stalkerin.gameru.net/wiki/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://stalkerin.gameru.net/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.226.13.119&amp;*</id>
		<title>S.T.A.L.K.E.R. Inside Wiki - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://stalkerin.gameru.net/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.226.13.119&amp;*"/>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/188.226.13.119"/>
		<updated>2026-04-29T16:54:44Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.22.6</generator>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=%D0%A1%D0%BF%D1%80%D0%B0%D0%B2%D0%BE%D1%87%D0%BD%D0%B8%D0%BA_%D0%BF%D0%BE_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F%D0%BC_%D0%B8_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0%D0%BC</id>
		<title>Справочник по функциям и классам</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%A1%D0%BF%D1%80%D0%B0%D0%B2%D0%BE%D1%87%D0%BD%D0%B8%D0%BA_%D0%BF%D0%BE_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F%D0%BC_%D0%B8_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0%D0%BC"/>
				<updated>2012-09-15T05:58:48Z</updated>
		
		<summary type="html">&lt;p&gt;188.226.13.119: /* сlass alife_simulator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Посты с форума AMK&lt;br /&gt;
&lt;br /&gt;
==Структура Lua_help==&lt;br /&gt;
Сам фаил можно найти в '''gamedata\scripts\lua_help.script'''&lt;br /&gt;
&amp;lt;code&amp;gt;C++ class [класс] {&lt;br /&gt;
     const [описание1] = [константа1];&lt;br /&gt;
     const [описание2] = [константа2];&lt;br /&gt;
     ...&lt;br /&gt;
     [константы заменяют описания при вызове методов классов];&lt;br /&gt;
     &lt;br /&gt;
     property [свойство1];&lt;br /&gt;
     property [свойство2];&lt;br /&gt;
     ...&lt;br /&gt;
[свойства (property) - это характеристика какого-либо класса, которая может подвергаться мат. операциям, слияниям, присвояниям и пр.&lt;br /&gt;
     читаются через точку: obj.health];&lt;br /&gt;
&lt;br /&gt;
     [глобальная(-ые) функция(-ии), которой вызывается класс];&lt;br /&gt;
     function [метод1] ([аргумент 1 функции 1], [аргумент 2 функции 1]...);&lt;br /&gt;
     function [метод2] ([аргумент 1 функции2]...);&lt;br /&gt;
             ...&lt;br /&gt;
     [методы - это специальные функции, выполняющие операции с классами и возвращающие значения после операций над классами.&lt;br /&gt;
     вызываются через двоеточие: obj:add_animation(...)];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Создание своего класса==&lt;br /&gt;
Свой класс можно добавить так:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
class &amp;quot;my_cool_class&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Здесь &amp;quot;my_cool_class&amp;quot; - имя моего нового класса. Записывается в кавычках, т.е. это строка.&lt;br /&gt;
&lt;br /&gt;
Потом можно добавлять к этому классу свои методы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
-- это специальный метод - конструктор. Будет вызван при создании класса.&lt;br /&gt;
function my_cool_class:__init(num)&lt;br /&gt;
    self.my_param = num -- добавили в свой класс переменную&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- обычный метод&lt;br /&gt;
function my_cool_class:cool_method_of_my_class()&lt;br /&gt;
    get_console():execute(&amp;quot;lua_bind_in_action_&amp;quot;..self.my_param)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- деструктор, вызывается при сборке объекта сборщиком мусора. Аргументов не имеет (кроме скрытого self. об этом см. далее)&lt;br /&gt;
function my_cool_class:__finalize()&lt;br /&gt;
    get_console():execute(&amp;quot;good_by_&amp;quot;..self.my_param)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Потом можно создавать экземпляры своего класса, вызывать их методы и пр.:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
local obj1 = my_cool_class(1) -- здесь создаётся новый объект. При этом вызывается конструктор и &lt;br /&gt;
                              -- ему передаются аргументы этого вызова (в данном случае число 1).&lt;br /&gt;
local obj2 = my_cool_class(4)&lt;br /&gt;
obj1:cool_method_of_my_class()&lt;br /&gt;
obj2:cool_method_of_my_class()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как только объект становится ненужным (т.е. на него больше нет ссылок) он удаляется сборщиком мусора. При этом вызывается его метод __finalize&lt;br /&gt;
&lt;br /&gt;
При чём здесь self.&lt;br /&gt;
self - это скрытый первый аргумент. Конструкция вида:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
function class_name:fun_name(&amp;lt;список аргументов&amp;gt;)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
на самом деле эквивалентна конструкции &lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
function class_name.fun_name(self, &amp;lt;список аргументов&amp;gt;)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Обратите внимание на двоеточие в первом случае и точку во втором.&amp;lt;br&amp;gt;&lt;br /&gt;
При вызове метода конструкция вида:&lt;br /&gt;
&lt;br /&gt;
'''object_name:fun_name(&amp;lt;список аргументов&amp;gt;)'''&lt;br /&gt;
&lt;br /&gt;
эквивалентна конструкции&lt;br /&gt;
&lt;br /&gt;
'''object_name.fun_name(object_name, &amp;lt;список аргументов&amp;gt;)'''&lt;br /&gt;
&lt;br /&gt;
Это означает, что в качестве self как правило передаётся сам же объект. &amp;quot;Как правило&amp;quot; - это потому, что можно в принципе передать что-то другое. Но лучше этого соглашения не нарушать.&lt;br /&gt;
&lt;br /&gt;
Всё это здорово, но какая от этого польза? Ну сделал я свой класс, и что с ним делать?&lt;br /&gt;
&lt;br /&gt;
В общем, классы - это неплохое средство организации своего кода. Владеющие объектно-ориентированным подходом найдут это расширение весьма удобным. В частности такие, ни к чему не привязанные классы, в скриптах сталкера используются в некоторых местах. Однако, настоящая сила этой технологии не в этом. С помощью этой технологии можно расширять классы, экспортированные в Lua со стороны движка игры. Об этом пойдет речь в следующей части.&lt;br /&gt;
&lt;br /&gt;
==Наследование от экспортированных классов==&lt;br /&gt;
Итак, кроме создания собственных классов технология Luabind позволяет создавать класы на основе уже существующих. Это реализовано в виде своеобразного наследования. Делается это с помощью следующей конструкции:&lt;br /&gt;
&lt;br /&gt;
'''class &amp;quot;my_derived_class&amp;quot; (base_class)'''&lt;br /&gt;
&lt;br /&gt;
Здесь:&lt;br /&gt;
&amp;quot;my_derived_class&amp;quot; как и раньше - это имя нового класса.&amp;lt;br&amp;gt;&lt;br /&gt;
base_class - это уже существующий класс, который будет базовым для вновь созданного. Имя выбрано произвольно, но подразумевается, что такой класс экспортирован и предназначен для наследования.&amp;lt;br&amp;gt;&lt;br /&gt;
Далеко не любой класс из экспортированных со стороны хост-приложения можно использовать в качестве базового. На стороне хост-приложения должен быть создан специальный класс-обёртка. Если его нет, то ничего хорошего не выйдет. Скорее всего при попытке использовать унаследованный класс в Lua будут малопонятные проблемы и вылеты, так что надо знать совершенно точно, можно ли от конкретного класса наследовать.&amp;lt;br&amp;gt;&lt;br /&gt;
Вопрос, какой глубины может быть наследование, т.е. можно ли наследовать от производного класса, мне пока до конца не ясен. С другой стороны, в этом практически нет необходимости.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итак после выполнения указанной конструкции появляется класс my_derived_class, который является копией класса base_class. У него есть в точности те же методы, что и у его базового класса. Пока от этого мало толку. Но теперь можно переопределить методы нашего класса, изменив таким образом его поведение. Делается это так же, как и ранее, но с некоторыми дополнениями.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
-- конструктор&lt;br /&gt;
function my_derived_class:__init(num) super(num)&lt;br /&gt;
end&lt;br /&gt;
-- обычный метод&lt;br /&gt;
function my_cool_class:some_method(&amp;lt;список аргументов&amp;gt;)&lt;br /&gt;
    base_class.some_method(self, &amp;lt;список аргументов&amp;gt;)&lt;br /&gt;
end&lt;br /&gt;
-- деструктор&lt;br /&gt;
function my_cool_class:__finalize()&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На что стОит обратить внимание.&amp;lt;br&amp;gt;&lt;br /&gt;
Во-первых, конструкция super(num). Это вызов конструктора базового класса.&amp;lt;br&amp;gt;&lt;br /&gt;
Во-вторых, для вызова метода базового класса из метода унаследованного используется другая конструкция base_class.some_method(self, ). Здесь надо использовать альтернативную форму вызова метода (с точкой и явным указанием первого аргумента self).&amp;lt;br&amp;gt;&lt;br /&gt;
Наконец, метод __finalize() не требует вызова этого же метода для базового класса, поскольку тот будет вызван автоматически при удалении объекта сборщиком мусора.&lt;br /&gt;
&lt;br /&gt;
Собственно и всё. Теперь можно пользоваться созданным классом: создавать объекты этого класса, вызывать их методы и пр. Здесь уже всё зависит от конкретных целей и требует знания конкретных классов.&lt;br /&gt;
&lt;br /&gt;
==сlass alife_simulator==&lt;br /&gt;
alife_simulator - класс для управления серверными объектами. В игре существует всего один экземпляр такого класса, и получить его можно с помощью вызова глобальной функции alife().&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
level_name(level_id) -- возвращает имя уровня по его id&lt;br /&gt;
create_ammo(&amp;lt;имя секции патронов&amp;gt;, position, lvid, gvid, id, amount) - создание пачек с патронами&lt;br /&gt;
create(&amp;lt;имя секции объекта&amp;gt;, position, lvid, gvid, id) -- создание объектов в инвентаре&lt;br /&gt;
create(&amp;lt;имя секции объекта&amp;gt;, position, lvid, gvid) -- создание объектов на уровне&lt;br /&gt;
во всех функция создания объектов&lt;br /&gt;
position - объект класса vector. Не может быть nil, иначе вылет&lt;br /&gt;
lvid - level vertex id&lt;br /&gt;
gvid - game vertex id&lt;br /&gt;
id - это куда спавним. Если в инвентарь, то указываем id непися или ноль (для актора)&lt;br /&gt;
release(sobj, boolean) -- удаляет объект sobj. Зачем второй булевский аргумент - не известно. &lt;br /&gt;
                          Во всех примерах он либо отсутствует, либо установлен в true&lt;br /&gt;
object(id) -- возвращает серверный объект по его id. Если не нашёл nil&lt;br /&gt;
story_object(sid) -- возвращает серверный объект по его story id. Если не нашёл nil&lt;br /&gt;
actor() -- возвращает серверный объект актора&lt;br /&gt;
set_switch_online(id, boolean) -- переводит в онлайн объект с идентификатором id&lt;br /&gt;
set_switch_offline(id, boolean) -- то же , но в оффлайн&lt;br /&gt;
dont_has_info(const number&amp;amp;, string); -- проверка на Неполученный инфопоршень(db.actor:dont_has_info(&amp;quot;infoportion&amp;quot;) &lt;br /&gt;
                                      -- или alife():actor():dont_has_info(&amp;quot;infoportion&amp;quot;)).&lt;br /&gt;
has_info - противоположная&lt;br /&gt;
&lt;br /&gt;
предположительно:&lt;br /&gt;
switch_distance() -- радиус переключения в оффлайн?&lt;br /&gt;
switch_distance(number) -- установка этого радиуса?&lt;br /&gt;
level_id() -- текущий номер уровня ?&lt;br /&gt;
object(string) -- получение объекта оп его имени. Не помню точно, что за имя.&lt;br /&gt;
object(number, boolean) - то же, что и object(number)? на что влияет второй аргумент?&lt;br /&gt;
valid_object_id(number) -- проверка наличия объекта по id?&lt;br /&gt;
alife():create( &amp;lt;индекс объекта в all.spawn&amp;gt; ) -- &lt;br /&gt;
spawn_id(number) -- индекс объекта в all.spawn для объекта с заданным id ?&lt;br /&gt;
&lt;br /&gt;
неизвестно:&lt;br /&gt;
add_out_restriction(cse_alife_monster_abstract*, number);&lt;br /&gt;
set_interactive(number, boolean);&lt;br /&gt;
add_in_restriction(cse_alife_monster_abstract*, number);&lt;br /&gt;
function remove_in_restriction(cse_alife_monster_abstract*, number);&lt;br /&gt;
remove_out_restriction(cse_alife_monster_abstract*, number);&lt;br /&gt;
kill_entity(cse_alife_monster_abstract*, const number&amp;amp;, cse_alife_schedulable*);&lt;br /&gt;
kill_entity(alife_simulator*, cse_alife_monster_abstract*, const number&amp;amp;);&lt;br /&gt;
kill_entity(alife_simulator*, cse_alife_monster_abstract*);&lt;br /&gt;
has_info(const number&amp;amp;, string);&lt;br /&gt;
remove_all_restrictions(number, const enum RestrictionSpace::ERestrictorTypes&amp;amp;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Пример использования:====&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
local sim = alife() -- получаем сам объект класса alife_simulator&lt;br /&gt;
local sactor = sim:actor() -- получаем серверный объект для актора&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==class vector==&lt;br /&gt;
vector - вспомогательный класс, содержащий три координаты, и позволяющий выполнять с ними различные манипуляции. Объекты класса vector являются аргументами многих функций и возвращаются многими функциями. См. например выше про метод create класса alife_simulator. Отдельный объект класса вектор создаётся вызовом глобальной функции vector(). При создании имеет координаты [0,0,0].&lt;br /&gt;
&lt;br /&gt;
===Список методов класса vector===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
v:set_length(number) - оставить направление, изменить длину&lt;br /&gt;
v:normalize(); - сделать длину единичной&lt;br /&gt;
&lt;br /&gt;
v:magnitude() -- длина вектора v&lt;br /&gt;
v1:distance_to(v2) -- расстояние между v1 и v2&lt;br /&gt;
v1:distance_to_sqr(v2) -- квадрат расстояния между v1 и v2&lt;br /&gt;
v1:crossproduct(v2) -- векторное произведение. Возвращает вектор&lt;br /&gt;
v1:dotproduct(v2) -- скалярное произведение. Возвращает число&lt;br /&gt;
v:sub(number); -- вычесть из каждого компонента v число number&lt;br /&gt;
v1:sub(v2); -- вычесть почленно вектор v2 из v1 и поместить в v1&lt;br /&gt;
v1:sub(v2, v3) -- вычесть из v2 - v3 и поместить в v1&lt;br /&gt;
аналогично:&lt;br /&gt;
add - почленное сложение&lt;br /&gt;
mul - почленное умножение&lt;br /&gt;
div - почленное деление&lt;br /&gt;
max/min - почленный максимум/минимум&lt;br /&gt;
v1:clamp(v2) -- почленное обрезание первого вторым&lt;br /&gt;
average -- почленное среднее&lt;br /&gt;
invert -- почленная инверсия знака&lt;br /&gt;
&lt;br /&gt;
v1:abs(v2); -- копирует почленно положительные компоненты в v1 из v2&lt;br /&gt;
lerp(v1, v2, number) -- линейная интерполяция между двумя векторами&lt;br /&gt;
-- если number больше 1, то выходит за них - экстраполяция&lt;br /&gt;
&lt;br /&gt;
v1:reflect(v2, v3) - должно быть отражение одного вектора от плоскости, определяемой другим. Не проверял&lt;br /&gt;
v1:slide(v2, v3); - вероятно, проекция одного вектора на плоскость, определяемую другим. Не уверен&lt;br /&gt;
v1:distance_to_xz(v2) - учитывая, что плоскость XZ - это плоскость параллельная земле, то возможно это &lt;br /&gt;
                        длина проекции на землю пути от точки до точки. Не проверял&lt;br /&gt;
&lt;br /&gt;
v:getP() - зенитный угол в радианах (угол наклона над плоскостью XZ )&lt;br /&gt;
v:getH() - азимутальный угол в радианах (угол в плоскости XZ между проекцией вектора на эту плоскость и осью Z)&lt;br /&gt;
setHP - соответственно для установки этих значений&lt;br /&gt;
v1:align() - выравнивает вектор вдоль оси x или z и нормирует его. Значение по y игнорируется. &lt;br /&gt;
             В итоге всегда возвращается одно из четырёх значений ([1,0,0], [-1,0,0], [0,0,1], [0,0,-1])&lt;br /&gt;
v4:mad(v1,v2,v3) -- бешеная операция =) умножить почленно второе на третье и сложить с &lt;br /&gt;
                    первым, т.е. v4 = v1 + v2*v3 (название операции mad == mul + add)&lt;br /&gt;
v1:inertion(v2, number) -- v1 = v2 + (v1-v2)*number (тоже почленно)&lt;br /&gt;
v1:similar(v2, number) -- сравнение векторов на равенство с погрешностью number. Выдаёт 1 или 0&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Пример использования:====&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
local v1 = vector()&lt;br /&gt;
v1:set(1,2,3)&lt;br /&gt;
local v2 = vector():set(4,5,6)&lt;br /&gt;
local dist = v1:distance_to(v2)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==class fcolor==&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
C++ class fcolor {&lt;br /&gt;
    property a; --альфа-цвет, отвечающий за прозрачность.&lt;br /&gt;
    property b; --blue-цвет, отвечающий за синий оттенок цвета.&lt;br /&gt;
    property g; --green-цвет, отвечающий за зелёный оттенок цвета.&lt;br /&gt;
    property r; --red-цвет, отвечающий за красный оттенок цвет.&lt;br /&gt;
&lt;br /&gt;
    fcolor ();&lt;br /&gt;
&lt;br /&gt;
    function set(number, number, number, number); --установка цвета (r,g,b,a)&lt;br /&gt;
    function set(const fcolor&amp;amp;); --установка цвета по уже имеющемуся классу &amp;quot;fcolor()&amp;quot;&lt;br /&gt;
    function set(number); -- не знаю. предполагаю, что есть несколько заранее подготовленных цветов с индексами для каждого.&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Пример использования:====&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
color = fcolor()&lt;br /&gt;
color.a = 50&lt;br /&gt;
color.g = 255&lt;br /&gt;
color.r = 100&lt;br /&gt;
color.b = 50&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
или&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
color = fcolor()&lt;br /&gt;
color:set(100,255,50,50)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==class hit==&lt;br /&gt;
Бывают случаи, при которых необходимо нанести некоторый урон неписю или мобу, при этом имитировав попадание в него. Для этого существует метод класса game_object:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
function hit(hit*);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Чтобы использовать этот метод, нужно указать аргументы. В данном случае имеет объект другого класса - hit. Вот его описание:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
C++ class hit {&lt;br /&gt;
    const burn = 0; --здесь идут константы типов урона, используемых в свойстве &amp;quot;type&amp;quot;. Все мы прекрасно знаем, что означает каждый.&lt;br /&gt;
    const chemical_burn = 6;&lt;br /&gt;
    const dummy = 11;&lt;br /&gt;
    const explosion = 7;&lt;br /&gt;
    const fire_wound = 8;&lt;br /&gt;
    const radiation = 4;&lt;br /&gt;
    const shock = 1;&lt;br /&gt;
    const strike = 2;&lt;br /&gt;
    const telepatic = 5;&lt;br /&gt;
    const wound = 3;&lt;br /&gt;
&lt;br /&gt;
    property direction; --дирекция урона&lt;br /&gt;
    property draftsman; --обозначает персонажа, нанёсшего урон.&lt;br /&gt;
    property impulse; --импульс урона&lt;br /&gt;
    property power; --сила урона&lt;br /&gt;
    property type; -- тип урона (см. константы)&lt;br /&gt;
&lt;br /&gt;
    hit ();&lt;br /&gt;
    hit (const hit*);&lt;br /&gt;
&lt;br /&gt;
    function bone(string); --метод с указанием кости урона&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Пример использования:====&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
hit = hit() -- создание объекта класса hit&lt;br /&gt;
hit.direction = vector():set(1,0,0) --дирекция по оси x&lt;br /&gt;
hit.draftsman = db.actor --нанёс урон актор&lt;br /&gt;
hit.impulse = 600 --импульс&lt;br /&gt;
hit.power = 1.45 --хит&lt;br /&gt;
hit.type = 2 --тип урона - выстрел&lt;br /&gt;
hit:bone(&amp;quot;bip01_head&amp;quot;) -- удар приходится на голову&lt;br /&gt;
obj:hit(hit)&lt;br /&gt;
-- наносим урон&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==&amp;quot;Функции времени&amp;quot;==&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
time_global() -- реальное время (в миллисекундах) с начала запуска программы&lt;br /&gt;
game.time() -- игровое время (в игровых миллисекундах) с начала игры (т.е. с начала прохождения игры)&lt;br /&gt;
level.get_time_days() -- день месяца по игровому времени&lt;br /&gt;
level.get_time_hours() -- час текущего игрового дня&lt;br /&gt;
level.get_time_minutes() -- минута текущего игрового часа&lt;br /&gt;
&lt;br /&gt;
level.get_time_factor() -- возвращает отношение скорости течения игрового времени к скорости реального &lt;br /&gt;
                        --(game_time_speed / real_time_speed)&lt;br /&gt;
level.set_time_factor(number) -- устанавливает это отношение&lt;br /&gt;
&lt;br /&gt;
game.get_game_time() -- возвращает игровое время в виде объекта класса CTime. Класс CTime я опишу подробно &lt;br /&gt;
                     -- как-нибудь потом, а пока опишу только пару его методов.&lt;br /&gt;
конструктор. Вызывается через пространство имён game&lt;br /&gt;
game.CTime()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Дефолтовые значения===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
year, month, day = 1 &lt;br /&gt;
hour, min, sek, ms = 0&lt;br /&gt;
CTime.set(year, month, day, hour, min, sek, ms) -- устанавливает все данные о времени&lt;br /&gt;
метод CTime.get описан так:&lt;br /&gt;
function get(number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;);&lt;br /&gt;
что наводит на мысль о семи аргументах. однако с точки зрения Lua этот метод не принимает аргументов, зато возвращает семь значений. &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
====Пример использования обоих методов:====&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
local t = game.CTime() -- создания объекта с дефолтовыми значнеиями&lt;br /&gt;
t:set(2009, 7, 11, 8, 11, 22, 333) -- установили все значения&lt;br /&gt;
&lt;br /&gt;
local y,m,d,h,min,sec,ms = t:get() -- получили все значения&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Класс CTime (неразобранный)===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
C++ class CTime {&lt;br /&gt;
const DateToDay = 0;&lt;br /&gt;
const DateToMonth = 1;&lt;br /&gt;
const DateToYear = 2;&lt;br /&gt;
const TimeToHours = 0;&lt;br /&gt;
const TimeToMilisecs = 3;&lt;br /&gt;
const TimeToMinutes = 1;&lt;br /&gt;
const TimeToSeconds = 2;&lt;br /&gt;
&lt;br /&gt;
CTime ();&lt;br /&gt;
CTime (const CTime&amp;amp;);&lt;br /&gt;
&lt;br /&gt;
function sub(CTime*);&lt;br /&gt;
function timeToString(number);&lt;br /&gt;
function dateToString(number);&lt;br /&gt;
operator ==(const CTime&amp;amp;, CTime);&lt;br /&gt;
function get(number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;, number&amp;amp;);&lt;br /&gt;
function set(number, number, number, number, number, number, number);&lt;br /&gt;
function setHMSms(number, number, number, number);&lt;br /&gt;
function diffSec(CTime*);&lt;br /&gt;
operator &amp;lt;(const CTime&amp;amp;, CTime);&lt;br /&gt;
operator +(CTime&amp;amp;, CTime);&lt;br /&gt;
operator &amp;gt;=(const CTime&amp;amp;, CTime);&lt;br /&gt;
function setHMS(number, number, number);&lt;br /&gt;
operator &amp;gt;(const CTime&amp;amp;, CTime);&lt;br /&gt;
operator -(CTime&amp;amp;, CTime);&lt;br /&gt;
operator &amp;lt;=(const CTime&amp;amp;, CTime);&lt;br /&gt;
function add(CTime*);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==класс CUIScriptWnd==&lt;br /&gt;
CUIScriptWnd(); --для работы с окном...&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    function _construct();&lt;br /&gt;
    function Register(CUIWindow*);&lt;br /&gt;
    function Register(CUIWindow*, string);&lt;br /&gt;
    function Enable(boolean);        --вкл/выкл&lt;br /&gt;
    function SetHeight(number);  --установка высоты&lt;br /&gt;
    function GetFrameLineWnd(string);&lt;br /&gt;
    function SetHolder(CDialogHolder*);&lt;br /&gt;
    function GetWidth() const;  --получение ширины контрола&lt;br /&gt;
    function GetCheckButton(string);--Получение состояния checkBox ,на выходе true или false?&lt;br /&gt;
    function DetachChild(CUIWindow*);&lt;br /&gt;
    function SetPPMode();&lt;br /&gt;
    function SetFont(CGameFont*);  --установка шрифта по имени&lt;br /&gt;
    function IsShown();                --проверка видно ли контрол иль нет?&lt;br /&gt;
    function Show(boolean);            --показывать контолили нет?&lt;br /&gt;
    function GetHeight() const;        --возвращает высоту&lt;br /&gt;
    function SetWidth(number);        --устанавливает ширину&lt;br /&gt;
    function GetListWndEx(string);&lt;br /&gt;
    function IsEnabled();&lt;br /&gt;
    function ResetPPMode();&lt;br /&gt;
    function GetPropertiesBox(string);&lt;br /&gt;
    function GetFont();                --возвращает шрифт установленный setFont?&lt;br /&gt;
    function Update();&lt;br /&gt;
    function AddCallback(string, number, const function&amp;lt;void&amp;gt;&amp;amp;); --соответские контрола с действие?&lt;br /&gt;
    function AddCallback(string, number, const function&amp;lt;void&amp;gt;&amp;amp;, object);--соответские контрола с действие?&lt;br /&gt;
    function GetButton(string);--Получаем последнюю нажатую кнопку?&lt;br /&gt;
    function SetAutoDelete(boolean);&lt;br /&gt;
    function OnKeyboard(number, enum EUIMessages);--Колбек нажатия на клавиатуре&lt;br /&gt;
    function Dispatch(number, number);&lt;br /&gt;
    function GetListWnd(string);--получение окна ListBoxa?&lt;br /&gt;
    function AttachChild(CUIWindow*);&lt;br /&gt;
    function GetStatic(string);--получение текста метки&lt;br /&gt;
    function SetWndPos(number, number);--установка позиции окна(x,y)&lt;br /&gt;
    function GetTabControl(string);&lt;br /&gt;
    function GetRadioButton(string);--состояние RadioButtton на выходите true или false&lt;br /&gt;
    function Init(number, number, number, number);--инициализация окна...читай FormInitializatione (x,y,width,height)&lt;br /&gt;
    function Init(Frect*);--инициализация окна&lt;br /&gt;
    function GetFrameWindow(string);&lt;br /&gt;
    function WindowName();&lt;br /&gt;
    function GetDialogWnd(string);&lt;br /&gt;
    function GetHolder();&lt;br /&gt;
    function SetWndRect(Frect);--установка координат углов&lt;br /&gt;
    function SetWndRect(number, number, number, number);--установка координат углов&lt;br /&gt;
    function GetEditBox(string);--получение текста из ЭдитБокса&lt;br /&gt;
    function SetWindowName(string);--Устанавливаем имя форме(caption)&lt;br /&gt;
    function GetProgressBar(string);&lt;br /&gt;
    function GetMessageBox(string);&lt;br /&gt;
    function SetWndSize(number, number);&lt;br /&gt;
    function Load(string);&lt;br /&gt;
    function IsAutoDelete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Авторы==&lt;br /&gt;
Статья создана: [[Участник:malandrinus|malandrinus]], [[Участник:IQDDD|IQDDD]], [[Участник:меченый|меченый]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>188.226.13.119</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%9B%D0%BE%D0%B3%D0%B8%D0%BA%D0%B0_NPC</id>
		<title>SoC. Логика NPC</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%9B%D0%BE%D0%B3%D0%B8%D0%BA%D0%B0_NPC"/>
				<updated>2012-07-21T09:35:16Z</updated>
		
		<summary type="html">&lt;p&gt;188.226.13.119: /* Разбор настройки и работы планировщика на примере скрипта xr_kamp */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Теория=&lt;br /&gt;
&lt;br /&gt;
Предполагается, что читатель этой статьи знаком с языком [http://www.lua.org/ LUA] и основами объектно-ориентированного программирования.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==История==&lt;br /&gt;
&lt;br /&gt;
Подход к решению проблемы игрового ИИ, выбранный создателями '''S.T.A.L.K.E.R.''' (далее буду писать просто &amp;quot;Сталкер&amp;quot;), был &lt;br /&gt;
впервые применён в 1957 году Гербертом Саймоном (Herbert Simon) и Алленом Ньюэллом (Allen Newell) в программе '''GPS''' &lt;br /&gt;
('''General Problem Solver''' или '''Универсальный Решатель Задач''').&lt;br /&gt;
&lt;br /&gt;
Суть этого подхода заключается в том, что сначала задача представляется в виде набора условий, набора операторов, &lt;br /&gt;
изменяющих эти условия, и описания начального и конечного состояний. Затем осуществляется поиск последовательности &lt;br /&gt;
операторов, переводящей начальное состояние системы в конечное.&lt;br /&gt;
&lt;br /&gt;
В Сталкере подсистема поиска последовательности операторов называется '''планировщик'''.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ИИ в Сталкере==&lt;br /&gt;
&lt;br /&gt;
Как видно, этот способ решения проблем не подходит для шутера, так как ситуация в игре может меняться &lt;br /&gt;
непредсказуемым образом и построенный план решения (последовательность операторов) станет неприменимым к текущей &lt;br /&gt;
ситуации. Поэтому планировщик запускается каждый раз при непредвиденном развитии событий и создаёт новую &lt;br /&gt;
последовательность действий (операторов).&lt;br /&gt;
&lt;br /&gt;
Условия в игре тоже вычисляются динамически. Для этого используются специальные объекты – эвалуаторы. Эвалуатор &lt;br /&gt;
должен содержать метод '''evaluate()''', возвращающий '''true''', если условие выполняется и '''false''' в противном случае. Операторы также представлены как объекты. Планировщик вызывает метод '''initialize()''' при начале работы оператора, затем он &lt;br /&gt;
периодически вызывает метод '''execute()'''.&lt;br /&gt;
&lt;br /&gt;
Например, можно создать эвалуатор для условия '''NPC голоден''', и привязать к этому условию оператор '''поесть'''. &lt;br /&gt;
&lt;br /&gt;
Планировщик будет периодически проверять это условие (вызывать метод '''evaluate()''' эвалуатора), и если оно выполняется, &lt;br /&gt;
инициализирует и будет выполнять оператор '''поесть''' до тех пор, пока условие не станет ложным.&lt;br /&gt;
&lt;br /&gt;
К сожалению, в большей части скриптов все возможности планировщика не используются. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Разбор настройки и работы планировщика на примере скрипта xr_kamp===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим скрипт '''xr_kamp''', заставляющий сталкеров сидеть у костра и рассказывать анекдоты.&lt;br /&gt;
Настройка планировщика осуществляется в функции '''add_to_binder'''. Параметры функции: '''object''' – объект, для которого &lt;br /&gt;
настраивается планировщик (в нашем случае это сталкер), '''ini''', '''scheme''', '''section''' – инициализационный файл, название &lt;br /&gt;
схемы действий, секция ини-файла (эти параметры будут подробно разобраны в части по созданию мода), '''storage''' – &lt;br /&gt;
таблица для хранения текущих параметров схемы действий.&lt;br /&gt;
&lt;br /&gt;
Разберём, что делает эта функция. &lt;br /&gt;
&lt;br /&gt;
Сначала получаем планировщик для текущего объекта ('''object'''):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
local manager = object:motivation_action_manager()&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Затем присваиваем идентификаторы операторов и условий элементам массива. Это сделано просто для удобства. &lt;br /&gt;
&lt;br /&gt;
Идентификаторы могут иметь любое целочисленное значение, главное, чтобы они были уникальными, то есть не &lt;br /&gt;
использовались для других операторов и условий.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
properties[&amp;quot;kamp_end&amp;quot;]=xr_evaluators_id.stohe_kamp_base+1&lt;br /&gt;
properties[&amp;quot;on_position&amp;quot;]=xr_evaluators_id.stohe_kamp_base+2&lt;br /&gt;
properties[&amp;quot;contact&amp;quot;]=xr_evaluators_id.stohe_meet_base+1&lt;br /&gt;
operators[&amp;quot;go_position&amp;quot;]=xr_actions_id.stohe_kamp_base+1&lt;br /&gt;
operators[&amp;quot;wait&amp;quot;]=xr_actions_id.stohe_kamp_base+3&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для каждого идентификатора условия создадим соответствующий эвалуатор и добавим его в планировщик. В данном случае &lt;br /&gt;
это условия: '''закончить ли посиделки около костра?''' и '''пришёл ли я на своё место у костра?'''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
manager:add_evaluator (properties[&amp;quot;kamp_end&amp;quot;],    this.evaluator_kamp_end    (&amp;quot;kamp_end&amp;quot;, storage, &amp;quot;kamp_end&amp;quot;))&lt;br /&gt;
manager:add_evaluator (properties[&amp;quot;on_position&amp;quot;], this.evaluator_on_position (&amp;quot;kamp_on_position&amp;quot;, storage, &amp;quot;kamp_on_position&amp;quot;))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь создадим оператор '''сидеть около костра, рассказывать анекдоты, жевать колбасу и т.д.'''. Можно было бы &lt;br /&gt;
реализовать эти действия как набор разных операторов, выбором которых занимался бы планировщик, но автор скрипта &lt;br /&gt;
решил сделать один сложный оператор.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
local action = this.action_wait (object:name(),&amp;quot;action_kamp_wait&amp;quot;, storage)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Задаем предусловия для этого оператора. Планировщик выберет этот оператор при выполнении всех условий. Всё это &lt;br /&gt;
значит примерно следующее: я могу сидеть у костра, если: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_alive, true))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
я живой,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_danger,false))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
опасностей нет,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_enemy, false))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
врагов нет,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_anomaly,false))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
аномалий поблизости нет,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
xr_motivator.addCommonPrecondition(action)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
выполняются другие важные условия (игрок не собирается со мной поговорить, я не собираюсь никого бить по морде, я не &lt;br /&gt;
ранен, я не собираюсь стрелять по вертолёту),&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(properties[&amp;quot;on_position&amp;quot;],  true))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
я уже нахожусь около костра.&lt;br /&gt;
&lt;br /&gt;
Скажем планировщику, что он должен ожидать от выполнения этого оператора. В данном случае после выполнения этого &lt;br /&gt;
оператора условие '''закончить ли посиделки около костра?''' должно стать истинным. То есть если условие стало истинным, &lt;br /&gt;
планировщик прекратит выполнение оператора.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_effect     (world_property(properties[&amp;quot;kamp_end&amp;quot;],   true))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Создание оператора завершено. Добавим его в планировщик.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
manager:add_action (operators[&amp;quot;wait&amp;quot;], action)&lt;br /&gt;
&amp;lt;/lua&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Эта строчка не имеет отношения к работе планировщика. Если коротко, то она позволяет объекту получать уведомления об &lt;br /&gt;
определённых событиях (смерть NPC – вызывается метод '''death_callback()''', попадание пули в NPC – вызывается метод &lt;br /&gt;
'''hit_callback()''' и т.д.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
xr_logic.subscribe_action_for_events(object, storage, action)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Создаем оператор, отвечающий за доставку NPC к его месту у костра.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action = this.action_go_position (object:name(),&amp;quot;action_go_kamp&amp;quot;, storage)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Добавляем предусловия, как и для предыдущего оператора.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_alive, true))&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_danger,false))&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_enemy, false))&lt;br /&gt;
action:add_precondition   (world_property(stalker_ids.property_anomaly,false))&lt;br /&gt;
xr_motivator.addCommonPrecondition(action)&lt;br /&gt;
action:add_precondition   (world_property(properties[&amp;quot;on_position&amp;quot;],  false))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Единственное отличие – последнее условие. Этот оператор будет выполняться только если NPC ещё не находится на своем &lt;br /&gt;
месте у костра, то есть если функция '''evaluator_on_position.evaluate()''' возвращает '''false'''.&lt;br /&gt;
&lt;br /&gt;
В результате выполнения этого действия условие '''на своём ли я месте у костра?''' должно стать истинным.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_effect     (world_property(properties[&amp;quot;on_position&amp;quot;],  true))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Создание оператора завершено. Добавляем его к планировщику.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
manager:add_action (operators[&amp;quot;go_position&amp;quot;], action)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Осталось ещё одна задача. Нужно запретить планировщику активировать оператор '''alife''', тот самый оператор, который &lt;br /&gt;
заставляет NPC болтаться по карте, отстреливать собачек и в конце концов попадать в аномалию. Впрочем, отстрелом &lt;br /&gt;
врагов занимается другой оператор с идентификатором '''stalker_ids.action_combat_planner'''.&lt;br /&gt;
&lt;br /&gt;
Для этого мы получаем оператор '''alife''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action = manager:action (xr_actions_id.alife)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
И добавляем к его предусловиям следующее: условие '''закончить ли посиделки у костра?''' должно быть истинным.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition   (world_property(properties[&amp;quot;kamp_end&amp;quot;],   true))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итак, мы настроили планировщик. Посмотрим как всё это будет работать.&lt;br /&gt;
&lt;br /&gt;
В некоторый момент времени гулаг, в который попал NPC, назначает ему работу: сидеть у костра. В результате условие &lt;br /&gt;
'''закончить ли посиделки у костра?''' становится ложным. Планировщик видит это изменение и пытается выработать &lt;br /&gt;
последовательность операторов, после выполнения которой, условие бы стало истинным и NPC снова бы вернулся к &lt;br /&gt;
выполнению высокоприоритетного оператора '''alife'''. Для выполнения этой задачи подходит оператор '''посиделки у костра''', &lt;br /&gt;
но для него не выполняется условие '''я на своем месте у костра'''. Поэтому планировщик создаёт план из двух операторов: &lt;br /&gt;
'''дойти до костра''' и '''посиделки у костра'''. Если во время выполнения одного из операторов возникнет непредвиденная &lt;br /&gt;
ситуация (появится враг, главный герой начнёт приставать с вопросами и т.п.), то планировщик скорректирует план, &lt;br /&gt;
добавив оператор для устранения этой непредвиденной ситуации.&lt;br /&gt;
&lt;br /&gt;
Как видно, система ИИ в Сталкере обладает весьма большой гибкостью, что мы и продемонстрируем при создании мода.&lt;br /&gt;
&lt;br /&gt;
==Модели (или схемы) поведения в Сталкере==&lt;br /&gt;
&lt;br /&gt;
В наборе скриптов Сталкера предусмотрена возможность объединять операторы и условия в модели поведения. Модель &lt;br /&gt;
поведения – это набор логически связанных операторов и условий, служащих для выполнения определённой задачи. Так &lt;br /&gt;
скрипт '''xr_kamp''' представляет собой модель поведения, состоящую из двух операторов и двух условий.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Регистрация модели поведения===&lt;br /&gt;
&lt;br /&gt;
Для включения новой модели поведения в набор моделей, доступных NPC, сначала необходимо её зарегистрировать. &lt;br /&gt;
Предположим, нам нужно зарегистрировать модель поведения, описанную в скрипте '''actor_need_help.script'''. Регистрация &lt;br /&gt;
моделей осуществляется в скрипте '''modules.script'''.  Добавим туда следующие строки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
if actor_need_help then –- в этой строке мы проверяем что наш скрипт действительно существует&lt;br /&gt;
    load_scheme(&amp;quot;actor_need_help&amp;quot;, &amp;quot;actor_need_help&amp;quot;, stype_stalker)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Первый параметр функции '''load_scheme''' задает имя файла скрипта, второй параметр – это название модели поведения, &lt;br /&gt;
третий параметр – тип модели поведения (возможны следующие значения: '''stype_stalker''' – модель поведения NPC, &lt;br /&gt;
'''stype_mobile''' – модель поведения монстра, '''stype_item''' – «модель поведения» физического объекта, '''stype_heli''' – модель &lt;br /&gt;
поведения вертолёта, '''stype_restrictor''' – «модель поведения» области пространства). Скрипты для моделей поведения &lt;br /&gt;
разных типов пишутся по-разному. Мы будем рассматривать только модели поведения NPC.&lt;br /&gt;
&lt;br /&gt;
Внимание! Для успешной работы модели поведения её скрипт должен содержать функцию '''add_to_binder''', выполняющую &lt;br /&gt;
настройку планировщика.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Активация/деактивация модели поведения===&lt;br /&gt;
&lt;br /&gt;
Некоторые модели поведения применимы в любых ситуациях (например, реакция на попадание пули в NPC или реакция на &lt;br /&gt;
появление врага). Такие модели должны активироваться/деактивироваться в функциях '''enable_generic_schemes()/disable_generic_schemes()''' скрипта '''xr_logic'''. В случае с моделью поведения '''actor_need_help''' это будет выглядеть так:&lt;br /&gt;
&lt;br /&gt;
'''1.''' Создаём функции '''set_actor_need_help''' и '''disable_scheme''' в нашем скрипте '''actor_need_help'''. Эти функции будут отвечать за активацию и деактивацию нашей модели поведения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function set_actor_need_help(npc,ini,scheme)&lt;br /&gt;
  local st=xr_logic.assign_storage_and_bind(npc, ini, scheme, “actor_need_help”)&lt;br /&gt;
  st.enabled=true&lt;br /&gt;
end&lt;br /&gt;
function disable_scheme(npc,scheme)&lt;br /&gt;
  local st = db.storage[npc:id()][scheme]&lt;br /&gt;
  if st then&lt;br /&gt;
    st.enabled = false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''2.''' Добавляем следующую строку в скрипт '''xr_logic.script'' после строки '''if stype == modules.stype_stalker then''' в &lt;br /&gt;
функции '''enable_generic_schemes()''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
  actor_need_help.set_actor_need_help(npc,ini,”actor_need_help”)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''3.''' Добавляем следующую строку в скрипт '''xr_logic.script''' после строки '''if stype == modules.stype_stalker then''' в &lt;br /&gt;
функции '''disable_generic_schemes()''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
  actor_need_help.disable_scheme(npc,”actor_need_help”)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если же модель поведения предназначена только для использования в определённых ситуациях, то достаточно выполнить &lt;br /&gt;
шаг '''1''' и использовать созданные функции по мере надобности. Например, активируя эту схему через диалог с NPC (как мы &lt;br /&gt;
и сделаем в нашем моде).&lt;br /&gt;
&lt;br /&gt;
Внимание! Я максимально упростил функции активации/деактивации модели поведения. Чтобы полностью разобраться с ними, &lt;br /&gt;
посмотрите скрипты '''xr_combat''', '''xr_kamp''' и другие подобные.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Приоритеты моделей поведения===&lt;br /&gt;
&lt;br /&gt;
Некоторые модели поведения настолько важны, что должны срабатывать в любой ситуации (например, реакция на попадание &lt;br /&gt;
пули). Для этого в скрипте '''xr_motivator''' предусмотрена функция '''addCommonPrecondition(action)''', в эту функцию можно &lt;br /&gt;
добавить одно из условий нашей модели поведения, чтобы другие модели поведения не могли сработать при выполнении &lt;br /&gt;
этого условия (здесь есть свои тонкости, но мы рассмотрим их позже). Предположим, что у нас есть модель поведения &lt;br /&gt;
'''actor_need_help''', заставляющая NPC подбежать к ГГ и вылечить его. Пусть за проверку здоровья ГГ отвечает условие с &lt;br /&gt;
идентификатором '''actor_need_help.property_actor_is_wounded'''. Значит, если мы хотим, чтобы NPC подбегал к ГГ, не обращая &lt;br /&gt;
внимание ни на что другое, то нужно добавить следующую строчку в функцию '''addCommonPrecondition(action)''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
action:add_precondition(world_property(actor_need_help.property_actor_is_wounded,false))&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта строчка запретит выполнение всех других действий, если условие с идентификатором &lt;br /&gt;
'''actor_need_help.property_actor_is_wounded''' станет истинным (в нашем случае это будет означать, что ГГ сильно ранен). &lt;br /&gt;
&lt;br /&gt;
Конкретное значение здоровья ГГ, при котором он считается сильно раненным, будет определять эвалуатор этого условия.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Создаем мод=&lt;br /&gt;
&lt;br /&gt;
В этом разделе мы сделаем мод, позволяющий сказать дружественно настроенному NPC, чтобы он лечил главного героя во &lt;br /&gt;
время боя.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Постановка задачи==&lt;br /&gt;
&lt;br /&gt;
Итак, мы хотим, чтобы дружественные NPC наконец начали приносить пользу. Для этого научим их лечить ГГ во время боя.&lt;br /&gt;
Распишем по пунктам:&lt;br /&gt;
&lt;br /&gt;
'''1.''' Нужно добавить дружественным NPC ветку в диалоге с просьбой присматривать за ГГ и лечить его, если в этом &lt;br /&gt;
возникнет необходимость.&lt;br /&gt;
&lt;br /&gt;
'''2.''' Добавить NPC модель поведения, реализующую выполнение этой просьбы.&lt;br /&gt;
&lt;br /&gt;
'''2.1.''' NPC должен действовать согласно этой модели только если ГГ находится недалеко от него.&lt;br /&gt;
&lt;br /&gt;
'''2.2.''' NPC не должен далеко отходить от ГГ во время боя.&lt;br /&gt;
&lt;br /&gt;
'''2.3.''' Если здоровье ГГ упало ниже определённой отметки, NPC должен подойти/подбежать и вылечить ГГ.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Что потребуется для реализации==&lt;br /&gt;
&lt;br /&gt;
Нам придётся изменять диалоги для некоторых NPC, для этого нужно будет изменить файлы &lt;br /&gt;
'''config\gameplay\character_dialogs.xml''' (диалоги для всех NPC), '''config\localization.ltx''' и '''config\system.ltx''' (подробнее см. статью '''BAC9-FLCL''' или '''Fr3nzy'''). Мы изменим диалоги для всех NPC, но для неподходящих NPC диалог будет отсекаться с помощью предусловия. Потребуется также добавить файлы с текстами диалогов и функции для проверки условий, &lt;br /&gt;
используемых в диалогах.&lt;br /&gt;
&lt;br /&gt;
Для включения новой модели поведения NPC нужно будет внести изменения в скрипты '''scripts\modules.script''' (регистрация &lt;br /&gt;
моделей поведения) и '''scripts\xr_motivator.script''' (для установки высокого приоритета нашей модели). Модификации файла &lt;br /&gt;
'''xr_logic.script''', в котором происходит установка общих моделей поведения, не потребуется, так как мы будем &lt;br /&gt;
активировать нашу схему поведения при выборе определённой ветки в диалоге.&lt;br /&gt;
&lt;br /&gt;
Теперь решим, какие условия и операторы нам понадобятся.&lt;br /&gt;
&lt;br /&gt;
Условия:&lt;br /&gt;
&lt;br /&gt;
'''1.''' Состояние главного героя. Если оно ниже определённого порога, то условие станет истинным. Назначим ему &lt;br /&gt;
идентификатор '''property_actor_is_wounded''' и эвалуатор '''evaluator_actor_is_wounded'''. Далее я буду указывать идентификатор &lt;br /&gt;
и эвалуатор в скобках через запятую.&lt;br /&gt;
&lt;br /&gt;
'''2.''' Находится ли NPC достаточно близко, чтобы вылечить ГГ ('''property_ready_to_heal, evaluator_ready_to_heal''').&lt;br /&gt;
&lt;br /&gt;
'''3.''' Есть ли у NPC аптечки ('''property_has_medkit''', '''evaluator_has_medkit''').&lt;br /&gt;
&lt;br /&gt;
'''4.''' Не отошёл ли NPC слишком далеко от ГГ или ГГ от NPC ('''property_faraway''', '''evaluator_faraway''').&lt;br /&gt;
&lt;br /&gt;
'''5.''' Находится ли ГГ достаточно близко, чтобы имело смысл помогать ему ('''property_near_enough''', '''evaluator_near_enough''').&lt;br /&gt;
&lt;br /&gt;
Операторы:&lt;br /&gt;
&lt;br /&gt;
'''1.''' Лечить ГГ ('''act_heal''', '''action_heal''').&lt;br /&gt;
&lt;br /&gt;
'''2.''' Подбежать к ГГ на дистанцию, достаточную для лечения ('''act_run_to_actor''', '''action_run_to_actor''').&lt;br /&gt;
&lt;br /&gt;
'''3.''' Крикнуть, что аптечки кончились ('''act_no_medkit''', '''action_no_medkit''').&lt;br /&gt;
&lt;br /&gt;
'''4.''' Подобраться поближе к ГГ, чтобы быть под рукой ('''act_stay_close''', '''action_stay_close''').&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
&lt;br /&gt;
Я буду писать эту статью параллельно с разработкой мода, указывая все найденные ошибки. Надеюсь, это поможет другим &lt;br /&gt;
моддерам. Чтобы отделить результаты тестирования от описания процесса разработки мода, я буду выделять свои &lt;br /&gt;
комментарии другим шрифтом.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Диалоги===&lt;br /&gt;
&lt;br /&gt;
В этом моде будет всего один диалог, и довольно простой, поэтому начнём с него. Создаём файл &lt;br /&gt;
'''config\gameplay\dialogs_need_help.xml'''. Чтобы не возиться с идентификаторами текстов, попробуем обойтись без них. &lt;br /&gt;
Начнём с простой тестовой версии:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;windows-1251&amp;quot; standalone=&amp;quot;yes&amp;quot; ?&amp;gt;&lt;br /&gt;
&amp;lt;game_dialogs&amp;gt;&lt;br /&gt;
    &amp;lt;dialog id=&amp;quot;actor_will_need_help&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;actor_need_help.i_am_friend&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;phrase_list&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Дружище, сможешь присмотреть за мной, если стрелять начнут? Промедольчику, там, вколоть или &lt;br /&gt;
перевязку сделать, если что...&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;1&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Нет проблем, конечно помогу.&amp;lt;/text&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
        &amp;lt;/phrase_list&amp;gt;&lt;br /&gt;
    &amp;lt;/dialog&amp;gt;&lt;br /&gt;
&amp;lt;/game_dialogs&amp;gt;&lt;br /&gt;
&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Добавляем строку с идентификатором этого диалога в '''config\gameplay\character_dialogs.xml''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&lt;br /&gt;
&amp;lt;actor_dialog&amp;gt;actor_will_need_help&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дописываем имя файла диалога в '''config\system.ltx''' в секцию '''dialogs'''. Осталось создать функцию '''i_am_friend'''. Наш &lt;br /&gt;
скрипт с моделью поведения будет называться '''scripts\actor_need_help.script''', заодно пропишем там и диалоговые &lt;br /&gt;
функции.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function i_am_friend(actor,npc)&lt;br /&gt;
  return npc:relation(actor)==game_object.friend&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При первом тестировании я заменил '''game_object.friend''' на '''game_object.neutral''', чтобы не искать друзей по всей карте. &lt;br /&gt;
&lt;br /&gt;
Тестируем, что у нас получилось...&lt;br /&gt;
&lt;br /&gt;
Диалог работает, но вместо текста – набор значков, оказалось, я написал текст в кодировке '''CP866 (DOS)''', нужно &lt;br /&gt;
поменять её на '''CP1251'''. Так, теперь текст в порядке.&lt;br /&gt;
&lt;br /&gt;
Выбранный подход к созданию диалога оказался удачным.&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, вместо идентификатора текста можно вписать сам текст. Это усложнит локализацию, но уменьшит время &lt;br /&gt;
создания диалогов.&lt;br /&gt;
&lt;br /&gt;
Теперь нам понадобятся функции проверки наличия аптечек у NPC, проверки активации схемы поведения, &lt;br /&gt;
активации/деактивации схемы поведения. Добавляем их в наш скрипт-файл.&lt;br /&gt;
&lt;br /&gt;
Первый вариант скрипта я не буду приводить, чтобы не увеличивать и так большую статью. Окончательный вариант &lt;br /&gt;
смотрите в конце статьи. Дальше по тексту идёт простейший работающий вариант.&lt;br /&gt;
&lt;br /&gt;
Теперь у нас должно быть два варианта начальной фразы для активной и неактивной схемы. Придётся экспериментировать.&lt;br /&gt;
&lt;br /&gt;
Вот первый проверенный вариант:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;windows-1251&amp;quot; standalone=&amp;quot;yes&amp;quot; ?&amp;gt;&lt;br /&gt;
&amp;lt;game_dialogs&amp;gt;&lt;br /&gt;
    &amp;lt;dialog id=&amp;quot;actor_will_need_help&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;actor_need_help.i_am_friend&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;phrase_list&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;1&amp;lt;/next&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;2&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Дружище, сможешь присмотреть за мной, если стрелять начнут? Промедольчику, там, вколоть или &lt;br /&gt;
перевязку сделать, если что...&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;precondition&amp;gt;actor_need_help.scheme_is_not_active&amp;lt;/precondition&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;11&amp;lt;/next&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;12&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;11&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Нет проблем, конечно помогу.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;precondition&amp;gt;actor_need_help.npc_have_medkit&amp;lt;/precondition&amp;gt;&lt;br /&gt;
                &amp;lt;action&amp;gt;actor_need_help.activate_scheme&amp;lt;/action&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;12&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Извини, друг, аптечек совсем не осталось.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;precondition&amp;gt;actor_need_help.npc_havent_medkit&amp;lt;/precondition&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Спасибо, друг, теперь я сам справлюсь.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;precondition&amp;gt;actor_need_help.scheme_is_active&amp;lt;/precondition&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;21&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;21&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Да не за что, ты мне помог, я - тебе.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;action&amp;gt;actor_need_help.deactivate_scheme&amp;lt;/action&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
        &amp;lt;/phrase_list&amp;gt;&lt;br /&gt;
    &amp;lt;/dialog&amp;gt;&lt;br /&gt;
&amp;lt;/game_dialogs&amp;gt;&lt;br /&gt;
&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При первом запуске игра вылетела без сообщений об ошибках. Я внимательно просмотрел все файлы и оказалось, что в &lt;br /&gt;
скриптах вместо комментария '''--''' (два минуса) я поставил просто минус (рекомендую пользоваться компилятором с &lt;br /&gt;
[http://www.lua.org www.lua.org] для проверки корректности скриптов). После исправления ошибки игра запустилась, но диалог так и не &lt;br /&gt;
появился. Небольшая дискуссия на форуме (спасибо Z.E.N. и Arhet) показала, что придётся сделать два диалога.&lt;br /&gt;
&lt;br /&gt;
Кроме того, при тестах первых вариантов, выяснилось, что всегда выбирается вариант диалога с просьбой о помощи. То &lt;br /&gt;
есть схема поведения не активируется. &lt;br /&gt;
&lt;br /&gt;
Как оказалось, в качестве параметров в функции передаются не объекты ГГ и NPC, а объекты говорящего в данный момент &lt;br /&gt;
персонажа и его собеседника. То есть, если фраза принадлежит NPC, то первый параметр будет объектом  для NPC, а не &lt;br /&gt;
для ГГ. Поэтому я изменил названия параметров и переписал функции.&lt;br /&gt;
&lt;br /&gt;
Итак, все проблемы решены. Файл диалога принял следующий вид ('''config\gameplay\dialogs_need_help.xml'''):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;windows-1251&amp;quot; standalone=&amp;quot;yes&amp;quot; ?&amp;gt;&lt;br /&gt;
&amp;lt;game_dialogs&amp;gt;&lt;br /&gt;
    &amp;lt;dialog id=&amp;quot;actor_will_need_help&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;actor_need_help.i_am_friend&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;actor_need_help.scheme_is_not_active&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;phrase_list&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Дружище, сможешь присмотреть за мной, если стрелять начнут? Промедольчику, там, вколоть или &lt;br /&gt;
перевязку сделать, если что...&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;11&amp;lt;/next&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;12&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;11&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;precondition&amp;gt;actor_need_help.npc_have_medkit&amp;lt;/precondition&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Нет проблем, конечно помогу.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;action&amp;gt;actor_need_help.activate_scheme&amp;lt;/action&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;12&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;precondition&amp;gt;actor_need_help.npc_havent_medkit&amp;lt;/precondition&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Извини, друг, аптечек совсем не осталось.&amp;lt;/text&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
        &amp;lt;/phrase_list&amp;gt;&lt;br /&gt;
    &amp;lt;/dialog&amp;gt;&lt;br /&gt;
    &amp;lt;dialog id=&amp;quot;actor_will_not_need_help&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;actor_need_help.i_am_friend&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;actor_need_help.scheme_is_active&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;phrase_list&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Спасибо, друг, теперь я сам справлюсь.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;21&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;21&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;Да не за что, ты мне помог, я - тебе.&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;action&amp;gt;actor_need_help.deactivate_scheme&amp;lt;/action&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
        &amp;lt;/phrase_list&amp;gt;&lt;br /&gt;
    &amp;lt;/dialog&amp;gt;&lt;br /&gt;
&amp;lt;/game_dialogs&amp;gt;&lt;br /&gt;
&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функции, поддерживающие работу диалога, теперь выглядят так (файл '''scripts\actor_need_help.script'''): &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function i_am_friend(talker,target)&lt;br /&gt;
  return target:relation(talker)==game_object.friend&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- За основу этой функции взята функция dialogs.actor_have_medkit&lt;br /&gt;
function npc_have_medkit(talker, target)&lt;br /&gt;
  return talker:object(&amp;quot;medkit&amp;quot;) ~= nil or&lt;br /&gt;
       talker:object(&amp;quot;medkit_army&amp;quot;) ~= nil or&lt;br /&gt;
       talker:object(&amp;quot;medkit_scientic&amp;quot;) ~= nil&lt;br /&gt;
end&lt;br /&gt;
function npc_havent_medkit(talker, target)&lt;br /&gt;
  return not npc_have_medkit(talker,target)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Так как модель поведения еще не написана, вставим заглушки&lt;br /&gt;
local scheme_status={}&lt;br /&gt;
function scheme_is_active(talker,target)&lt;br /&gt;
  return scheme_status[target:id()]==true -- сравниваем с true, чтобы функция не возвращала nil&lt;br /&gt;
end&lt;br /&gt;
function scheme_is_not_active(talker,target)&lt;br /&gt;
  return not scheme_is_active(talker,target)&lt;br /&gt;
end&lt;br /&gt;
function activate_scheme(talker,target)&lt;br /&gt;
  scheme_status[talker:id()]=true&lt;br /&gt;
end&lt;br /&gt;
function deactivate_scheme(talker,target)&lt;br /&gt;
  scheme_status[talker:id()]=nil -- присваиваем nil, чтобы освободить память, занятую этим элементом массива&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
И в файл '''config\gameplay\character_dialogs.xml''' добавлены строки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&lt;br /&gt;
&amp;lt;actor_dialog&amp;gt;actor_will_need_help&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
&amp;lt;actor_dialog&amp;gt;actor_will_not_need_help&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате получился работающий диалог, но NPC выглядит просто как ходячая аптечка. В окончательном варианте, я &lt;br /&gt;
добавил некоторые «человеческие» реакции. Да и сама модель поведения пока отсутствует - вместо неё стоят заглушки. Исправим это.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Модель поведения===&lt;br /&gt;
&lt;br /&gt;
Начнём создание модели поведения с разработки эвалуаторов. Эвалуатор должен представлять собой объект класса унаследованного от класса '''property_evaluator'''. &lt;br /&gt;
&lt;br /&gt;
Возьмём для начала эвалуатор '''evaluator_faraway''', определяющий, что NPC находится слишком далеко от ГГ. Этот эвалуатор требуется для того, чтобы NPC не отходил слишком далеко от ГГ и мог в случае надобности быстро подбежать к нему и оказать помощь. &lt;br /&gt;
&lt;br /&gt;
Объявляем класс эвалуатора:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
class &amp;quot;evaluator_faraway&amp;quot; (property_evaluator)&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Определяем функцию инициализации (в LUA это аналог конструктора объекта):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function evaluator_faraway:__init(name, storage) super (nil, name)&lt;br /&gt;
  self.st = storage&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ключевое слово '''super''' служит для вызова конструктора базового класса. Член '''st''' будет хранить ссылку на таблицу состояния нашей модели поведения.&lt;br /&gt;
&lt;br /&gt;
Теперь нужно определить функцию '''evaluate()''', ради которой и создавался эвалуатор. По-видимому всё просто, нужно проверить расстояние от NPC до ГГ и вернуть '''true''', если это расстояние больше определённого значения. Но давайте подумаем. Когда эвалуатор возвратит '''true''', заработает оператор, заставляющий NPC подойти поближе к ГГ, то есть расстояние моментально уменьшится и эвалуатор начнёт возвращать '''false''', что приведёт к переходу NPC под управление игрового ИИ. ИИ может опять решить удалиться от ГГ, что приведёт к повторному срабатыванию эвалуатора. В результате возникнет замкнутый цикл, и NPC будет крутиться на одном месте (на самом деле этот цикл рано или поздно разорвётся из-за изменения игровой ситуации, но лучше вообще избежать его).&lt;br /&gt;
&lt;br /&gt;
Можно использовать разные пути для решения этой проблемы. Попробуем сделать так: будем использовать два расстояния, эвалуатор сработает при достижении первого и будет оставаться активным, пока расстояние не станет меньше второго.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
local min_faraway_dist=10&lt;br /&gt;
local max_faraway_dist=20&lt;br /&gt;
&lt;br /&gt;
function evaluator_faraway:evaluate()&lt;br /&gt;
  local actor=db.actor&lt;br /&gt;
  if not actor then&lt;br /&gt;
  -- ГГ ещё не заспаунился&lt;br /&gt;
    return false&lt;br /&gt;
  end&lt;br /&gt;
  local dist=actor:position():distance_to(self.object:position())&lt;br /&gt;
  if dist&amp;gt;max_faraway_dist then&lt;br /&gt;
    self.st.faraway=true&lt;br /&gt;
  elseif dist&amp;lt;min_faraway_dist then&lt;br /&gt;
    self.st.faraway=false&lt;br /&gt;
  end&lt;br /&gt;
  return self.st.faraway==true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эвалуатор готов, но нужно как-то его протестировать. Поэтому давайте создадим минимальную модель поведения из одного условия и одного оператора. Нам нужен оператор, перемещающий NPC поближе к ГГ. Объявляем класс '''action_stay_close''', унаследованный от '''action_base''', и определяем его конструктор.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
class &amp;quot;action_stay_close&amp;quot; (action_base)&lt;br /&gt;
&lt;br /&gt;
function action_stay_close:__init(name, storage) super (nil, name)&lt;br /&gt;
  self.st=storage&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оператор должен содержать функции '''initialize()''', '''execute()''' и, возможно, '''finalize()'''.&lt;br /&gt;
&lt;br /&gt;
Функция '''initialize()''' вызывается при начала работы оператора, то есть в момент, когда планировщик ставит этот оператор в первую позицию плана.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function action_stay_close:initialize()&lt;br /&gt;
  local npc=self.object&lt;br /&gt;
  -- Не знаю зачем эти две функции, но они используются во всех операторах&lt;br /&gt;
  npc:set_desired_position()&lt;br /&gt;
  npc:set_desired_direction()&lt;br /&gt;
  -- Сбрасываем текущие анимации&lt;br /&gt;
  npc:clear_animations()&lt;br /&gt;
  -- Задаём параметры движения&lt;br /&gt;
  npc:set_detail_path_type(move.line)&lt;br /&gt;
  npc:set_body_state(move.standing)&lt;br /&gt;
  npc:set_movement_type(move.run)&lt;br /&gt;
  npc:set_path_type(game_object.level_path)&lt;br /&gt;
  -- Эксперименты показали, что эта функция устанавливает скорость движения (anim.danger - &lt;br /&gt;
минимальная скорость, anim.free - нормальная, anim.panic - максимальная)&lt;br /&gt;
  npc:set_mental_state(anim.panic)&lt;br /&gt;
  -- Повышаем зоркость NPC&lt;br /&gt;
  npc:set_sight(look.danger, nil, 0)&lt;br /&gt;
  -- Освободим сталкера от всех идиотских ограничений&lt;br /&gt;
  npc:remove_all_restrictions()&lt;br /&gt;
-- Зададим смещение точки назначения, чтобы помощники не сбивались в кучу&lt;br /&gt;
  self.offset=vector():set(math.random()*6-3,0,math.random()*6-3)&lt;br /&gt;
  self.offset:normalize()&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция '''execute()''' периодически вызывается во время выполнения оператора. Частота вызовов, по-видимому, зависит от расстояния от NPC до ГГ.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function action_stay_close:execute()&lt;br /&gt;
  local npc=self.object&lt;br /&gt;
  local actor=db.actor&lt;br /&gt;
  if not actor then&lt;br /&gt;
  -- Хм, что-то не так. Может быть ГГ перешёл на другой уровень? Запрещаем схему поведения&lt;br /&gt;
    self.st.enabled=false&lt;br /&gt;
  end&lt;br /&gt;
  -- Получаем ближайшую доступную точку в 5 метрах от ГГ&lt;br /&gt;
  -- Сначала, я попробавал использовать функцию npc:vertex_in_direction, но она не работает&lt;br /&gt;
  local vertex_id=level.vertex_in_direction(actor:level_vertex_id(),self.offset,5)&lt;br /&gt;
  local act_v_id=actor:level_vertex_id()&lt;br /&gt;
  -- Отправляем нашего NPC в найденную точку&lt;br /&gt;
  local acc_id=utils.send_to_nearest_accessible_vertex( npc, vertex_id )&lt;br /&gt;
  if self.st.dist and self.st.dist&amp;gt;max_faraway_dist then&lt;br /&gt;
-- если NPC находится слишком далеко от ГГ пусть пробежиться побыстрее&lt;br /&gt;
    npc:set_mental_state(anim.panic)&lt;br /&gt;
  else&lt;br /&gt;
    npc:set_mental_state(anim.free)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция настройки планировщика.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function add_to_binder(object, char_ini, scheme, section, st)&lt;br /&gt;
  local manager = object:motivation_action_manager()&lt;br /&gt;
  local property_wounded = xr_evaluators_id.sidor_wounded_base&lt;br /&gt;
&lt;br /&gt;
  -- Удаляем эвалуатор, так как в xr_motivator мы установили его в property_evaluator_const&lt;br /&gt;
  manager:remove_evaluator(property_faraway)&lt;br /&gt;
  -- и заменяем его нашим&lt;br /&gt;
  manager:add_evaluator(property_faraway, evaluator_faraway(&amp;quot;evaluator_faraway&amp;quot;,st))&lt;br /&gt;
    -- Создаём оператор&lt;br /&gt;
  local action = action_stay_close(&amp;quot;action_stay_close&amp;quot;,st)&lt;br /&gt;
    -- и настраиваем предусловия. 1. Сталкер жив&lt;br /&gt;
  action:add_precondition(world_property(stalker_ids.property_alive, true))&lt;br /&gt;
  -- 2. Сталкер не ранен&lt;br /&gt;
  action:add_precondition(world_property(property_wounded, false))&lt;br /&gt;
  -- Я использую свой мод для обхода аномалий, иначе от помощников мало толку.&lt;br /&gt;
  if anomaly_evader then&lt;br /&gt;
  -- 3. Рядом нет аномалий&lt;br /&gt;
    action:add_precondition (world_property(1099,false))&lt;br /&gt;
  end&lt;br /&gt;
 -- 4. Сталкер слишком далеко от ГГ&lt;br /&gt;
   action:add_precondition(world_property(property_faraway, true))&lt;br /&gt;
  action:add_effect (world_property(property_faraway, false))&lt;br /&gt;
  -- Добавляем оператор в планировшик&lt;br /&gt;
  manager:add_action (act_stay_close, action)&lt;br /&gt;
  -- Теперь подкорректируем стандартные операторы, чтобы помощник не отвлекался на всякую ерунду.&lt;br /&gt;
  action=manager:action(stalker_ids.action_alife_planner)&lt;br /&gt;
  action:add_precondition(world_property(property_faraway, false))&lt;br /&gt;
  action=manager:action(stalker_ids.action_combat_planner)&lt;br /&gt;
  action:add_precondition(world_property(property_faraway, false))&lt;br /&gt;
  action=manager:action(stalker_ids.action_danger_planner)&lt;br /&gt;
  action:add_precondition(world_property(property_faraway, false))&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Добавим функции активации/деактивации схемы поведения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function set_help(npc, ini)&lt;br /&gt;
  local st = xr_logic.assign_storage_and_bind(npc, ini, &amp;quot;actor_need_help&amp;quot;)&lt;br /&gt;
  st.enabled=true&lt;br /&gt;
end&lt;br /&gt;
function disable_scheme(npc, scheme)&lt;br /&gt;
  local st = db.storage[npc:id()][scheme]&lt;br /&gt;
  if st then&lt;br /&gt;
    st.enabled = false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Изменим диалоговые функции-заглушки.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function activate_scheme(talker,target)&lt;br /&gt;
  set_help(talker,talker:spawn_ini())&lt;br /&gt;
  scheme_status[talker:id()]=true&lt;br /&gt;
end&lt;br /&gt;
function deactivate_scheme(talker,target)&lt;br /&gt;
  disable_scheme(talker,&amp;quot;actor_need_help&amp;quot;)&lt;br /&gt;
  scheme_status[talker:id()]=nil&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Добавим в функцию '''xr_motivator.addCommonPrecondition()''' следующие строки, чтобы заблокировать стандартные схемы поведения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
  if actor_need_help then&lt;br /&gt;
    action:add_precondition (world_property(actor_need_help.property_faraway,false))&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если попробовать запустить мод сейчас, то игра просто вылетит. Причина в том, что мы добавили предусловие для стандартных схем поведения, но не добавили эвалуатор этого условия. Поэтому добавляем в функцию '''xr_motivator.net_spawn()''' следующие строки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
    local manager = self.object:motivation_action_manager()&lt;br /&gt;
    if actor_need_help then&lt;br /&gt;
      manager:add_evaluator(actor_need_help.property_faraway, property_evaluator_const(false))&lt;br /&gt;
    end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для того чтобы снизить нагрузку на процессор, используем '''property_evaluator_const''', который всегда возвращает одно и тоже значение. В результате тестирования выяснилось, что не все NPC подчиняются нашей схеме поведения. Причины этого пока не ясны, требуется дополнительное тестирование и очень желательна помощь разработчиков (хотя бы для того чтобы узнать как выяснить какой оператор действует в данный момент на NPC).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Файлы мода===&lt;br /&gt;
&lt;br /&gt;
Мод можно скачать [http://sdk.stalker-game.com/ru/images/4/47/%D0%9D%D0%B0%D0%BF%D0%B0%D1%80%D0%BD%D0%B8%D0%BA%D0%B8_0.2.zip здесь].&lt;br /&gt;
&lt;br /&gt;
Ссылка обновлена. Выложена новая версия. Причиной &amp;quot;зависаний&amp;quot; NPC был конфликт со '''state_mgr.script'''. Код мода, приведённый в статье, не устанавливал состояние NPC с помощью '''state_mgr.set_state()''', в результате '''state_mgr''' пытался вернуть состояние NPC к начальному (до срабатывания нашей модели поведения), что и приводило к &amp;quot;зависаниям&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Более подробное описание мода смотри [http://sdk.stalker-game.com/ru/index.php?title=Mod:%D0%9D%D0%B0%D0%BF%D0%B0%D1%80%D0%BD%D0%B8%D0%BA%D0%B8 здесь].&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Спасибо за внимание,&lt;br /&gt;
Red75&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Авторы=&lt;br /&gt;
&lt;br /&gt;
Статья создана:&lt;br /&gt;
&lt;br /&gt;
* [[Участник:Red75|Red75]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:A-Life]][[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>188.226.13.119</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=%D0%98%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B4%D0%B5%D1%84%D0%B5%D0%BA%D1%82%D0%BE%D0%B2_%D1%81%D0%B3%D0%BB%D0%B0%D0%B6%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BD%D0%B0_%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8F%D1%85...</id>
		<title>Исправление дефектов сглаживания на импортированных моделях...</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%98%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B4%D0%B5%D1%84%D0%B5%D0%BA%D1%82%D0%BE%D0%B2_%D1%81%D0%B3%D0%BB%D0%B0%D0%B6%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BD%D0%B0_%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8F%D1%85..."/>
				<updated>2011-08-03T19:31:18Z</updated>
		
		<summary type="html">&lt;p&gt;188.226.13.119: /* ...в 3ds Max */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==...в MilkShape 3D==&lt;br /&gt;
&amp;lt;!--FIXME!!!--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итак, в конвертере бардака, а так же (естественно) в его плагине для импорта моделей в 3Д редакторы есть одна крайне неприятная особенность: слетают группы сглаживания (или как там это правильно называется), и, когда потом модель через СДК экспортируется в ОГФ на модели возникают дефекты такого вида:&lt;br /&gt;
&lt;br /&gt;
[[Файл:Ms3d bad smooth.jpg||400px]]&lt;br /&gt;
&lt;br /&gt;
Что сделать для исправления (способ найден и проверен в '''Milkshape 3D''', как это делать в других редакторах не знаю, так как не пользуюсь)&lt;br /&gt;
&lt;br /&gt;
Выделяем всю модель или нужную область, потом ''Vertex - Weld Together'' и... наша моделька ровная и гладенькая&lt;br /&gt;
&lt;br /&gt;
[[Файл:Ms3d bad smooth_2.jpg||400px]]&lt;br /&gt;
&lt;br /&gt;
Теперь её можно спокойно экспортировать в СДК.&lt;br /&gt;
 &lt;br /&gt;
'''Т.е. напрямую загонять модели в СДК (через преобразование конвертером) не рекомендую во избежании этих дефектов, а они будут 100%'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==...в 3ds Max==&lt;br /&gt;
&lt;br /&gt;
По просьбе '''blackraven6''' добавляю способ по решению этой проблемы в '''3D Max''' (за что ему отдельное спасибо)&lt;br /&gt;
&lt;br /&gt;
В '''3D Max''' выделяем модель, далее ''Modifiers - Subdivision surface - Mesh smooth'' и справа в свитке ''subdivisions amount'' в поле ''iterations'' ставим вместо 1 (по дефолту) 0. &lt;br /&gt;
Тогда модели добавляется только визуальный эффект сглаживания без добавления дополнительных сглаживающих полигонов. &lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Ещё один способ, предложенный '''adept''' для '''3D Max''' &lt;br /&gt;
&lt;br /&gt;
[[Файл:3dsmax bad smooth.jpg||400px]]&lt;br /&gt;
&lt;br /&gt;
Выделяем модель и переходим на вкладку ''Modify'' (1)&lt;br /&gt;
&lt;br /&gt;
Открываем вкладку модификаторов (2)&lt;br /&gt;
&lt;br /&gt;
Выбираем модификатор ''Smooth'' (3)&lt;br /&gt;
&lt;br /&gt;
Модель стала граненная&lt;br /&gt;
&lt;br /&gt;
[[Файл:3dsmax bad smooth_2fx.jpg||400px]]&lt;br /&gt;
&lt;br /&gt;
Теперь нужно поставить галочку ''Auto Smooth''. Все, модель сгладилась. Для регулировки уровня сглаживания нужно поставить галочку ''Prevent Indirect Smoothing'' и клацать на кнопочки возле надписи ''Threshold'', стандартно сглаживание стоит на цифре 30&lt;br /&gt;
&lt;br /&gt;
[[Файл:3dsmax bad smooth_3fx.jpg||400px]]&lt;br /&gt;
&lt;br /&gt;
* Также можно использовать другой способ: открываем редактирование в Editable Mesh, нажимаем Polygon (или Element), выделяем всю модель и находим в редактировании полигонов квадратик из 32 (вроде там столько) чисел. Нажимаем &amp;quot;1&amp;quot; и модель сглажена.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color = &amp;quot;gray&amp;quot;&amp;gt;&lt;br /&gt;
По-человечески это называется &amp;quot;задать группы сглаживания&amp;quot; :) --[[Участник:RedPython|RedPython]] 18:04, 17 апреля 2011 (UTC)&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Авторы: '''Deathdoor''' (OGSE team), '''blackraven6''', '''adept''', [[Участник:Rekongstor|Rekongstor]]&lt;br /&gt;
&lt;br /&gt;
==...в Maya==&lt;br /&gt;
&lt;br /&gt;
Если в импортированной, например, из 3ds Max'а модели имеются видимые артефакты, то нужно разблокировать нормали, выполнив '''''Normals--&amp;gt;Unlock Normals''''' и затем '''''Normals--&amp;gt;Set to Face''''', при этом в опциях '''''Set to Face''''' должен стоять флажок '''''Match face normal'''''.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Maya bad face normals 1.png|400px]]  [[Файл:Maya bad face normals 2.png|400px]]&lt;br /&gt;
&lt;br /&gt;
Т.к. в Maya нет понятия &amp;quot;группы сглаживания&amp;quot;, а есть вершинные нормали, то поиск несуществующего пункта меню поначалу ставит в тупик новичка. Выходим из положения, выполнив '''''Shift+RMB--&amp;gt;Soften/Harden Edge--&amp;gt;Soften Edge''''':&lt;br /&gt;
&lt;br /&gt;
[[Файл:Maya bad face normals 3fx.png|400px]]  [[Файл:Maya smooth face normals.png|325px]]&lt;br /&gt;
&lt;br /&gt;
--[[Участник:RedPython|RedPython]] 18:04, 17 апреля 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
[[Категория:Wireframe]]&lt;/div&gt;</summary>
		<author><name>188.226.13.119</name></author>	</entry>

	</feed>