<?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=91.217.2.224&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=91.217.2.224&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/91.217.2.224"/>
		<updated>2026-04-29T13:41:13Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.22.6</generator>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=%D0%A1%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2%D1%8B%D0%B5_%D1%88%D0%B5%D0%B9%D0%B4%D0%B5%D1%80%D1%8B</id>
		<title>Скриптовые шейдеры</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%A1%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2%D1%8B%D0%B5_%D1%88%D0%B5%D0%B9%D0%B4%D0%B5%D1%80%D1%8B"/>
				<updated>2014-10-10T16:16:26Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Редактируя любую модель, многие в свойствах поверхностей замечали графу shader, в которой указывается нечто вроде models/model или подобные.&lt;br /&gt;
Как правило, эти шейдеры упакованы в '''shaders.xr''' и '''shaders_xrlc.x'''r, которые представляют собой своеобразную базу данных шейдеров, собранных по определенным шаблонам.&lt;br /&gt;
&lt;br /&gt;
Но кроме этих баз данных есть еще и скриптовые шейдеры, которые имеют приоритет перед теми шейдерами, что упакованы в *.xr. Это файлы, имеющие расширение .s и лежащие в папках соответствующих рендеров. Вычислить правильное название шейдера в БД и шейдерного скрипта '''.s''' можно очень просто - для примера, если у нас в БД шейдер находится в '''model/selflight''', то имя шейдера должно быть '''model_selflight.s''', если details/blend, то скрипт шейдера будет, соответственно, '''details_blend.s'''. Не так уж сложно.&lt;br /&gt;
&lt;br /&gt;
Вообще база данных с шейдерами была выдумана не потому что авторам сталкера очень нравилось плодить экземпляры труднораспарсиваемых форматов, а причина более банальна - чтобы увеличить скорость загрузки игры, сэкономив за счет активности жесткого диска при открытии файлов скриптовых шейдеров в папке соответствующего рендера. Плюс к тому в этом случае уменьшается количество шейдерных файлов, что как минимум вдвое уменьшает вероятность ошибки - при условии двух рендеров эта экономия получается за счет тех шейдеров, которые являются общими для обоих рендеров. Плюс к тому вероятность ошибки еще более уменьшается тем, что базы данных редактируются в сдк, а сдк эти базы данных представляет в виде всевозможных галочек и полей ввода, поэтому ошибка может быть только во вводимых данных, но не в скриптовом коде шейдеров, который, вероятнее всего, генерируется во время загрузки игры.&lt;br /&gt;
&lt;br /&gt;
Скриптовые шейдеры используются же в основном только в тех случаях, когда их код отличается для разных рендеров.&lt;br /&gt;
&lt;br /&gt;
По некоторым причинам я считаю что для модостроителей шейдерные скрипты являются более предпочтительными, чем распространение своих шейдерных модов в виде модифицированной .xr-БД. Причины в основном две: Во-первых скриптовые шейдеры предоставляют бОльшие возможности для полета фантазии, а во-вторых распространение шейдерных модов в виде скриптовых шейдеров все же делает меньше вероятность конфликта модов, так как разные шейдеры будут в разных файлах, а не все вместе в одном хитром и трудноредактируемом, и, что самое плохое, - труднообъединяемом файле. Поэтому далее мы рассмотрим скриптовые шейдеры.&lt;br /&gt;
&lt;br /&gt;
Что собой представляют шейдерные скрипты? Это скрипты на HLSL (High Level Shader Language), которые являются своеобразным языком для того чтобы объяснить движку, какие загружать текстуры и какие подключать шейдеры из наличествующих .ps и .vs файлов. &lt;br /&gt;
&lt;br /&gt;
: ''Для тех кто воспрял духом сразу скажу, что несмотря на то, что шейдерные скрипты написаны на языке, который используется в игровых скриптах, это все-таки не одно и то же. Во-первых шейдерные скрипты выполняются при загрузке уровня всего один раз (в фазу загрузки &amp;quot;загрузка шейдеров&amp;quot;), поэтому на отрисовку в реальном времени влиять неспособны, и кроме того, к игровым скриптам скриптовые шейдеры доступа не имеют.''&lt;br /&gt;
&lt;br /&gt;
Вот, для примера, содержимое простейшего скриптового шейдера:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;lua&amp;gt;function normal( shader, t_base, t_second, t_detail )&lt;br /&gt;
     shader:begin( &amp;quot;effects_gradient&amp;quot;, &amp;quot;effects_gradient_p&amp;quot; )&lt;br /&gt;
 end&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Разберем его по косточкам:&lt;br /&gt;
&lt;br /&gt;
* '''function normal''' - объявление стандартной функции шейдерного скрипта. В некоторых шейдерах есть еще функции '''l_spot''', '''l_point''', '''l_special''' и '''normal_hq''' аналогичного содержания, но явно просчитывающие поведение этого шейдера в условиях освещения&lt;br /&gt;
&lt;br /&gt;
* '''shader''' - вероятнее всего идентификатор шейдера в игре, содержимое этой переменной обычно представляет собой текстовую строку вроде &amp;quot;models_selflight&amp;quot; или &amp;quot;models/selflight&amp;quot;.&lt;br /&gt;
* '''t_base''' - путь к текстуре, которую следует выводить с помощью этого шейдера&lt;br /&gt;
* '''t_second''', '''t_detail''' - пути к другим текстурам, пока назначение их неизвестно.&lt;br /&gt;
&lt;br /&gt;
* '''shader:begin( &amp;quot;effects_gradient&amp;quot;, &amp;quot;effects_gradient_p&amp;quot; )''' - святая святых шейдера. Если точнее - создание объекта шейдера как такового. &lt;br /&gt;
:: '''effects_gradient''' - имя .vs файла используемого в этом шейдере&lt;br /&gt;
:: '''effects_gradient_p''' - имя .ps файла соответственно.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
После этой строки могут быть такие строки (они необязательны)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* ''': blend		( true, blend.srcalpha, blend.one )''' - настройка блендинга для текстур с альфой, например, полупрозрачные текстуры без применения этой строки будут выглядеть непрозрачными черными в прозрачных местах. Эти методы блендинга соответствуют переключателю в сдк, где варианты ALPHA-ADD, BLEND, SET и так далее. На примере выше блендинг соответствует положению ALPHA-ADD.&lt;br /&gt;
&lt;br /&gt;
:: '''true''' - может быть '''false''' - включение обработки текстур, то есть true - обработка включена, false, соответственно, выключена.&lt;br /&gt;
&lt;br /&gt;
:: '''blend.srcalpha''', '''blend.one''' - соответственно, методы блендинга, могут быть:&lt;br /&gt;
&lt;br /&gt;
 blend.one&lt;br /&gt;
 blend.zero&lt;br /&gt;
 null&lt;br /&gt;
&lt;br /&gt;
Также вот таблица, по которой можно выбрать остальные параметры блендинга, выбирая в таблице по порядку слева направо:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table style=&amp;quot;border: 1px solid #666;&amp;quot; width=&amp;quot;470px&amp;quot; height=&amp;quot;60px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;amp;nbsp;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td bgcolor=&amp;quot;#666&amp;quot; width=&amp;quot;2px&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;src&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td bgcolor=&amp;quot;#666&amp;quot; width=&amp;quot;2px&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;color&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td height=&amp;quot;2px&amp;quot; bgcolor=&amp;quot;#666&amp;quot; colspan=&amp;quot;5&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;inv&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td bgcolor=&amp;quot;#666&amp;quot; width=&amp;quot;2px&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;dest&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td bgcolor=&amp;quot;#666&amp;quot; width=&amp;quot;2px&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alpha&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
то есть:&lt;br /&gt;
&lt;br /&gt;
 blend.srccolor&lt;br /&gt;
 blend.invsrccolor&lt;br /&gt;
 blend.destcolor&lt;br /&gt;
 blend.invdestcolor&lt;br /&gt;
 blend.srcalpha&lt;br /&gt;
 blend.invsrcalpha&lt;br /&gt;
 blend.destalpha&lt;br /&gt;
 blend.invdestalpha&lt;br /&gt;
&lt;br /&gt;
: где '''inv''' - инверсия, '''color''' - цвет, '''alpha''' - альфа (коэффициент прозрачности), '''src''' - параметр берется до шейдера, '''dest''' - параметр берется после шейдера.&lt;br /&gt;
&lt;br /&gt;
* ''': sorting ( 3, false )''' - порядок отрисовки поверхности по отношению к самому объекту, частью которого является поверхность.&lt;br /&gt;
&lt;br /&gt;
:: '''3''' - цифра порядка отрисовки, где 1 - за объектом, 2 - на уровне с объектом, 3 - спереди объекта (ближе к актору). Как правило, при тройке шейдируемая поверхность будет отрисовываться поверх всего, в том числе и оружия.&lt;br /&gt;
&lt;br /&gt;
:: '''false''' - соответствует галочке &amp;quot;srict sorting&amp;quot; в сдк. Пока назначение неизвестно.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* ''': aref ( false, 2 )''' - функция переключает режим тестирования пикселей по альфе. Первый аргумент - вкл/выкл, второй аргумент - значение альфы, используемое в сравнении альфы пикселей. Если пиксель проверку не прошел, он не будет отрисовываться. Как именно происходит проверка альфы, пока непонятно, функция проверки устанавливается где-то в движке.&lt;br /&gt;
&lt;br /&gt;
* ''': zb ( false, false )''' - устанавливает функцию сравнения пикселей при z-тесте и включает режим записи в z-буфер для текстуры. Первый аргумент - функция сравнения (false - D3DCMP_LESSEQUAL - пиксель проходит тест только, если его z меньше или равно текущему пикселю, true - D3DCMP_ALWAYS - пиксель всегда проходит тест). Второй аргумент - включение записи в z-буфер, тут вкл/выкл.&lt;br /&gt;
&lt;br /&gt;
* ''': fog ( false )''' - отключает освещение текстуры и включает для нее FOG DirectX.&lt;br /&gt;
&lt;br /&gt;
* ''': emissive ( true )''' - применяется в источниках освещения.&lt;br /&gt;
&lt;br /&gt;
* ''': distort ( true )''' - представляет из себя механизм искажений, аналогичный эффекту искажающегося воздуха в некоторых аномалиях, а также &amp;quot;линза&amp;quot; в главном меню над кнопками.&lt;br /&gt;
&lt;br /&gt;
* ''': wmark (true)''' - используется в валлмарках&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''shader : sampler( &amp;quot;s_base&amp;quot; )''' - создание буфера для помещения туда текстуры. Аргумент представляет собой идентификатор, получаемый ps и vs шейдерами, где будет фигурировать как predefined-переменная s_base.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Перечисленные ниже параметры могут быть только ниже сэмплерной строки!''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* ''': texture  ( t_base ) ''' - помещение в вышеобъявленный буфер текстуры. Аргумент этой ф-ции представляет собой путь к текстуре, так что вполне можно указать нечто вроде&lt;br /&gt;
&lt;br /&gt;
 : texture  ( &amp;quot;fx\\fx_sun&amp;quot; )&lt;br /&gt;
&lt;br /&gt;
чтобы в буфер воткнуть текстуру '''gamedata/textures/fx/fx_sun.dds'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Следующие функции устанавливают один из режимов адресации текстуры. Режим адресации определяет способ обработки текстурных координат при выходе их за пределы диапазона [0.0,1.0]&lt;br /&gt;
* ''': clamp()''' - режим адресации D3DTADDRESS_CLAMP. Значения текстурных координат, выходящие за рамки [0.0,1.0] обрезаются до 0.0 и 1.0 соответственно. Соответствует галке Texture Clamp в сдк. &lt;br /&gt;
&lt;br /&gt;
* ''': wrap ()''' - режим адресации D3DTADDRESS_WRAP. При выходе значений текстурных координат за пределы [0.0,1.0] от значений отнимается их целая часть. Другими словами, если у вас u,v получились [1.3, 2.5], в этом режиме семплирование текстуры будет происходит по координатам [0.3, 0.5].&lt;br /&gt;
&lt;br /&gt;
* ''': mirror ()''' - режим адресации D3DTADDRESS_MIRROR. При выходе значений текстурных координат за пределы [0.0,1.0] от значений отнимается их целая часть и остаток вычитается из 1.0. Если u,v получились [1.3, 2.6], в этом режиме семплирование текстуры будет происходит по координатам [0.7, 0.4].&lt;br /&gt;
&lt;br /&gt;
Функции установки фильтрации текстур:&lt;br /&gt;
&lt;br /&gt;
* ''': f_none ()''' - точечная фильтрация (то есть по сути, нет фильтрации)&lt;br /&gt;
&lt;br /&gt;
* ''': f_linear ()''' - линейная фильтрация&lt;br /&gt;
&lt;br /&gt;
* ''': f_bilinear ()''' - билинейная фильтрация&lt;br /&gt;
&lt;br /&gt;
* ''': f_trilinear ()''' - трилинейная фильтрация. На редкость бесполезная функция, потому что семплер по умолчанию создается с трилинейной фильтрацией&lt;br /&gt;
&lt;br /&gt;
* ''': f_anisotropic ()''' - анизотропная фильтрация.&lt;br /&gt;
&lt;br /&gt;
Помимо этих функций также присутствуют функции для &amp;quot;тонкой&amp;quot; настройки фильтрации:&lt;br /&gt;
Настройка magnification filter&lt;br /&gt;
&lt;br /&gt;
* ''': fmag_none ()''' - без фильтрации&lt;br /&gt;
&lt;br /&gt;
* ''': fmag_point ()''' - точечная фильтрация&lt;br /&gt;
&lt;br /&gt;
* ''': fmag_linear ()''' - линейная фильтрация&lt;br /&gt;
&lt;br /&gt;
Настройка minification filter&lt;br /&gt;
&lt;br /&gt;
* ''': fmin_none ()''' - без фильтрации&lt;br /&gt;
&lt;br /&gt;
* ''': fmin_point ()''' - точечная фильтрация&lt;br /&gt;
&lt;br /&gt;
* ''': fmin_linear ()''' - линейная фильтрация&lt;br /&gt;
&lt;br /&gt;
* ''': fmin_aniso ()''' - анизотропная фильтрация.&lt;br /&gt;
&lt;br /&gt;
Настройка mip filter&lt;br /&gt;
&lt;br /&gt;
* ''': fmip_none ()''' - без фильтрации&lt;br /&gt;
&lt;br /&gt;
* ''': fmip_point ()''' - точечная фильтрация&lt;br /&gt;
&lt;br /&gt;
* ''': fmip_linear ()''' - линейная фильтрация&lt;br /&gt;
&lt;br /&gt;
Разные функции:&lt;br /&gt;
&lt;br /&gt;
* ''': project()''' - функция выставляет флаг D3DTTFF_PROJECTED для преобразования текстурных координат перед растеризатором. При выставленном флаге все компоненты текстурных координат делятся на w-компоненту. Актуально только для ffp (R1, ps_1_x).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Также в папке с шейдерами лежат еще файлы .s (именно такого имени - точка и эс), которые являются для всех *.s-файлов своеобразными заголовочными файлами, функции из файла .s доступны из файлов *.s . На данный момент там имеются функции printf, и закомментированные во втором рендере ф-ции l_point и l_spot, которые отвечают за создание точечного и неточечного источников света соответственно.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Статья создана участником cjayho, отредактировал ошибки Sергей][akaMoonlight'''&lt;br /&gt;
&lt;br /&gt;
[[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=G_always_run</id>
		<title>G always run</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=G_always_run"/>
				<updated>2014-09-20T11:58:03Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: Отмена правки 13622, сделанной участником 95.70.2.3 (обс.)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Описание ==&lt;br /&gt;
&lt;br /&gt;
Бег без усталости.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Входные параметры ==&lt;br /&gt;
&lt;br /&gt;
g_always_run on/off&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Значение по умолчанию ==&lt;br /&gt;
&lt;br /&gt;
Профиль настроек:&lt;br /&gt;
*Extreme - off&lt;br /&gt;
*Hight - off&lt;br /&gt;
*Default - off&lt;br /&gt;
*Low - off&lt;br /&gt;
*Minimum - off&lt;br /&gt;
&lt;br /&gt;
[[Категория:Команды консоли]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D1%80%D0%B5%D0%BC%D0%BE%D0%BD%D1%82_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BB%D0%B5%D0%BA%D1%82%D0%BE%D0%B2</id>
		<title>SoC. Создание ремонт комплектов</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D1%80%D0%B5%D0%BC%D0%BE%D0%BD%D1%82_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BB%D0%B5%D0%BA%D1%82%D0%BE%D0%B2"/>
				<updated>2014-01-10T10:39:22Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: /* Работа со скриптами */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание ремонт комплекта===&lt;br /&gt;
Я опишу пример создания ремонт комплекта для костюмов, а для оружия в конце статьи сделаю пару заметок. Дело в том, что различия в создании ремонт комплектов почти нет.&lt;br /&gt;
 &lt;br /&gt;
1. Для начала нам нужно создать сам ремонт комплект, то есть предмет, а уже потом вешать на него функцию при использование. Для этого в config\misc\items.ltx в конец добавляем конфиг нашего предмета:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;ini&amp;gt;[repair_kit_outfit]:identity_immunities&lt;br /&gt;
GroupControlSection	= spawn_group&lt;br /&gt;
discovery_dependency    =&lt;br /&gt;
$spawn 			= &amp;quot;food and drugs\repair kit outfit&amp;quot;&lt;br /&gt;
$prefetch 		= 32&lt;br /&gt;
class			= II_ANTIR ;класс не трогаем!&lt;br /&gt;
cform                   = skeleton&lt;br /&gt;
visual                  = equipments\item_merger.ogf ;модель&lt;br /&gt;
description		= enc_equipment_repair_kit_outfit ;ссылка на описание в config\text\rus\....xml&lt;br /&gt;
&lt;br /&gt;
inv_name		= repair_kit_outfit_name ;ссылка на название в config\text\rus\....xml&lt;br /&gt;
inv_name_short		= repair_kit_outfit_name ;тоже самое, что и inv_name&lt;br /&gt;
inv_weight		= 6.0 ;вес&lt;br /&gt;
&lt;br /&gt;
inv_grid_width		= 2 ;ширина иконки&lt;br /&gt;
inv_grid_height		= 1 ;высота иконки&lt;br /&gt;
inv_grid_x		= 8 ;координата x иконки&lt;br /&gt;
inv_grid_y		= 18 ;координата y иконки&lt;br /&gt;
cost			= 10000 ;цена&lt;br /&gt;
;всё ниже написанное не трогаем, оно нам и не нужно&lt;br /&gt;
eat_health = 0&lt;br /&gt;
eat_satiety = 0&lt;br /&gt;
eat_power = 0&lt;br /&gt;
eat_radiation = 0&lt;br /&gt;
wounds_heal_perc = 0&lt;br /&gt;
eat_portions_num = 1&lt;br /&gt;
animation_slot = 4&lt;br /&gt;
hud = wpn_vodka_hud&amp;lt;/ini&amp;gt;&lt;br /&gt;
2. Теперь добавим название и описание. Для этого в файле config\text\rus\string_table_enc_equipment.xml в конец между &amp;lt;/string&amp;gt; и &amp;lt;/string_table&amp;gt; добавляем строки по аналогии с уже написсанными: &lt;br /&gt;
       &amp;lt;xml&amp;gt;&amp;lt;string id=&amp;quot;repair_kit_outfit_name&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;text&amp;gt;Ремонт комплект ДПК&amp;lt;/text&amp;gt;&lt;br /&gt;
    &amp;lt;/string&amp;gt;&lt;br /&gt;
        &amp;lt;string id=&amp;quot;enc_equipment_repair_kit_outfit&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;text&amp;gt;Новинка от Вани Болта - ремонт комплект ДПК для быстрой и эффективной починки костюмов. При его создании были учтены все недочёты и, следовательно, удалены. Благодаря улучшенным инструментам и деталям, а также облегченному весу весьма ценен. С его помощью можно увеличить состояние костюма до 100%, если он повреждён не больше 30%. Незаменимая вещь во время вылазки.&amp;lt;/text&amp;gt;&lt;br /&gt;
    &amp;lt;/string&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
Отлично, с предметом закончили. Теперь приступим к самому главному - скриптам. &lt;br /&gt;
===Работа со скриптами===&lt;br /&gt;
1.Вешаем функцию починки при использовании нашего ремонт комплекта. &lt;br /&gt;
Для этого в файле '''scripts\bind_stalker.script''' &lt;br /&gt;
находим функцию:&lt;br /&gt;
&lt;br /&gt;
''function actor_binder:net_destroy()'' и после строки &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.take_item_from_box, nil)&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
пишем: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.use_object, nil)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
И теперь находим функцию&lt;br /&gt;
&lt;br /&gt;
''function actor_binder:reinit()'' и после строки &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.take_item_from_box, self.take_item_from_box, self)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
пишем: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.use_object, self.repair_outfit_start, self)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь в папке '''scripts''' создаёте свой скриптовый файл '''ваше_название_скрипта.script'''. Но! Название должно быть написано на английском языке и вместо пробелов должен стоять знак &amp;quot;_&amp;quot;. Опять открываем '''bind_stalker.script''' и в самый конец добавляем эту функцию: &lt;br /&gt;
&amp;lt;lua&amp;gt;function actor_binder:repair_outfit_start(obj, who)&lt;br /&gt;
    if obj then &lt;br /&gt;
        if obj:section() == &amp;quot;repair_kit_outfit&amp;quot; then --здесь указываем предмет, при использование которого будет срабатывать функция ниже&lt;br /&gt;
        ваше_название_скрипта.repair_outfit() --здесь указываем ссылку на функцию. Сначала скрипт, в котором она написана, потом название самой функции&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
2. Добавляем саму функцию ремонта. &lt;br /&gt;
Для этого в файле '''ваше_название_скрипта.script''' пишем: &lt;br /&gt;
&amp;lt;lua&amp;gt; -- Ремонт комплект для костюмов&lt;br /&gt;
function repair_outfit()&lt;br /&gt;
local item_s6 = db.actor:item_in_slot(6) --объявляем переменную (item_s6)&lt;br /&gt;
    if item_s6 ~= nil and item_s6:condition() &amp;gt;=0.30 and item_s6:condition() ~=1 then --когда костюм можно чинить (состояние больше или равно 30%)&lt;br /&gt;
        item_s6:set_condition(1)&lt;br /&gt;
        local snd_obj = xr_sound.get_safe_sound_object([[interface\inv_detach_addon]])&lt;br /&gt;
        snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0)&lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,0,255,0]Ремонт костюма завершен.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
    elseif item_s6 == nil then --когда костюма нет &lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,255,0,0]Ремонт невозможен: нужно одеть коcтюм.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
        alife():create(&amp;quot;repair_kit_outfit&amp;quot;, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())&lt;br /&gt;
    elseif item_s6:condition() &amp;lt;0.30 then --когда костюм нельзя чинить (состояние меньше 30%)&lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,255,0,0]Ремонт невозможен: костюм слишком сильно повреждён.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
        alife():create(&amp;quot;repair_kit_outfit&amp;quot;, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())&lt;br /&gt;
    elseif item_s6:condition() ==1 then --когда костюм в отличном состояние&lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,255,0,0]Ремонт невозможен: %c[255,0,255,0]костюм в отличном состояние.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
        alife():create(&amp;quot;repair_kit_outfit&amp;quot;, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())&lt;br /&gt;
    end&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
Всё! Теперь заходим в игру и наслаждаемся.&lt;br /&gt;
&lt;br /&gt;
===Небольшая заметка===&lt;br /&gt;
1. Чтобы сделать ремонт комплект для оружия проделываем тоже самое, только переименовываем всё вместо outfit на weapon и в функцие починки ставим слот 1 или 2 (оружейные слота) вместо 6 (слот костюмов).&lt;br /&gt;
Ещё нужно заменить переменную item_s6 например на item_s1 или item_s2.&lt;br /&gt;
&lt;br /&gt;
2. Число 0.30 - минимальное состояние костюма, следовательно его можно менять на любое вам угодное.&lt;br /&gt;
Простите за такое оформление, это моя вторая статья. В будущем, если будет больше свободного времени, постораюсь расписать все моменты подробно. Автор статьи: Бага.&lt;br /&gt;
&lt;br /&gt;
Данный метод создания ремонт комплектов не работает! (проверено)&lt;br /&gt;
ЗЫ: StalkerNew2012 этот скрипт подправил теперь всё работает :)&lt;br /&gt;
Ремонтный комплект без лишних сообщений просто съедается!:(&lt;br /&gt;
&lt;br /&gt;
3.Ремонт оружия и брони:&lt;br /&gt;
{| class=&amp;quot;collapsible collapsed&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;code lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
--     bind_stalker.script&lt;br /&gt;
function actor_binder:repair_outfit_start(obj, who)&lt;br /&gt;
    if obj then &lt;br /&gt;
        if obj:section() == &amp;quot;repair_kit_outfit&amp;quot; then&lt;br /&gt;
                remkomplekt.action_repair() &lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--     remkomplekt.script&lt;br /&gt;
function action_repair()&lt;br /&gt;
	-- эмулируем возврат ремкомплекта в инвентарь&lt;br /&gt;
	alife():create(&amp;quot;repair_kit_outfit&amp;quot;, vector(),0,0,0)&lt;br /&gt;
&lt;br /&gt;
	-- нет ли рядом врагов и монстров&lt;br /&gt;
	local apos = db.actor:position()&lt;br /&gt;
	for obj_id,_ in pairs (db.storage)do&lt;br /&gt;
		local obj = level.object_by_id(obj_id)&lt;br /&gt;
		if obj and (IsStalker(obj) or IsMonster(obj)) and obj:alive() &lt;br /&gt;
		and obj:relation(db.actor) == game_object.enemy and obj:position():distance_to(apos)&amp;lt;30 then&lt;br /&gt;
			send_tip(&amp;quot;Нельзя чинить костюм, когда враги слишком близко.&amp;quot;)&lt;br /&gt;
			return&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- пробуем чинить предмет в &lt;br /&gt;
	repair_item_in_slot(1) -- первом&lt;br /&gt;
	repair_item_in_slot(2) -- втором&lt;br /&gt;
	repair_item_in_slot(6) -- шестом слоте&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function repair_item_in_slot(num)&lt;br /&gt;
	local item = db.actor:item_in_slot(num)&lt;br /&gt;
	if item then&lt;br /&gt;
		local cond = item:condition()&lt;br /&gt;
		if cond &amp;lt; 0.3 then&lt;br /&gt;
			send_tip(&amp;quot;%c[255,255,0,0]Ремонт невозможен: предмет в слоте &amp;quot;..num.. &amp;quot; сильно повреждён.&amp;quot;)&lt;br /&gt;
		elseif cond &amp;gt; 0.95 then&lt;br /&gt;
			send_tip(&amp;quot;%c[255,255,0,0]Ремонт невозможен: %c[255,0,255,0]предмет в слоте &amp;quot;..num.. &amp;quot; в отличном состоянии.&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			item:set_condition(1)&lt;br /&gt;
			send_tip(&amp;quot;%c[255,0,255,0]Ремонт предмета в слоте &amp;quot;..num.. &amp;quot; завершен.&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		send_tip(&amp;quot;%c[255,255,0,0]В слоте &amp;quot;..num..&amp;quot; нет предмета для починки.&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function send_tip(news_text)&lt;br /&gt;
	db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
4. Для многоразового использования ремкомплекта можно не доспавнивать новый предмет при съедании, а в его конфиге указать для параметра '''eat_portions_num''' большое число.&lt;br /&gt;
&amp;lt;lua&amp;gt;eat_portions_num = 100500&amp;lt;/lua&amp;gt;&lt;br /&gt;
[[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D1%80%D0%B5%D0%BC%D0%BE%D0%BD%D1%82_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BB%D0%B5%D0%BA%D1%82%D0%BE%D0%B2</id>
		<title>SoC. Создание ремонт комплектов</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D1%80%D0%B5%D0%BC%D0%BE%D0%BD%D1%82_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BB%D0%B5%D0%BA%D1%82%D0%BE%D0%B2"/>
				<updated>2014-01-10T10:35:57Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: /* Небольшая заметка */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Создание ремонт комплекта===&lt;br /&gt;
Я опишу пример создания ремонт комплекта для костюмов, а для оружия в конце статьи сделаю пару заметок. Дело в том, что различия в создании ремонт комплектов почти нет.&lt;br /&gt;
 &lt;br /&gt;
1. Для начала нам нужно создать сам ремонт комплект, то есть предмет, а уже потом вешать на него функцию при использование. Для этого в config\misc\items.ltx в конец добавляем конфиг нашего предмета:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;ini&amp;gt;[repair_kit_outfit]:identity_immunities&lt;br /&gt;
GroupControlSection	= spawn_group&lt;br /&gt;
discovery_dependency    =&lt;br /&gt;
$spawn 			= &amp;quot;food and drugs\repair kit outfit&amp;quot;&lt;br /&gt;
$prefetch 		= 32&lt;br /&gt;
class			= II_ANTIR ;класс не трогаем!&lt;br /&gt;
cform                   = skeleton&lt;br /&gt;
visual                  = equipments\item_merger.ogf ;модель&lt;br /&gt;
description		= enc_equipment_repair_kit_outfit ;ссылка на описание в config\text\rus\....xml&lt;br /&gt;
&lt;br /&gt;
inv_name		= repair_kit_outfit_name ;ссылка на название в config\text\rus\....xml&lt;br /&gt;
inv_name_short		= repair_kit_outfit_name ;тоже самое, что и inv_name&lt;br /&gt;
inv_weight		= 6.0 ;вес&lt;br /&gt;
&lt;br /&gt;
inv_grid_width		= 2 ;ширина иконки&lt;br /&gt;
inv_grid_height		= 1 ;высота иконки&lt;br /&gt;
inv_grid_x		= 8 ;координата x иконки&lt;br /&gt;
inv_grid_y		= 18 ;координата y иконки&lt;br /&gt;
cost			= 10000 ;цена&lt;br /&gt;
;всё ниже написанное не трогаем, оно нам и не нужно&lt;br /&gt;
eat_health = 0&lt;br /&gt;
eat_satiety = 0&lt;br /&gt;
eat_power = 0&lt;br /&gt;
eat_radiation = 0&lt;br /&gt;
wounds_heal_perc = 0&lt;br /&gt;
eat_portions_num = 1&lt;br /&gt;
animation_slot = 4&lt;br /&gt;
hud = wpn_vodka_hud&amp;lt;/ini&amp;gt;&lt;br /&gt;
2. Теперь добавим название и описание. Для этого в файле config\text\rus\string_table_enc_equipment.xml в конец между &amp;lt;/string&amp;gt; и &amp;lt;/string_table&amp;gt; добавляем строки по аналогии с уже написсанными: &lt;br /&gt;
       &amp;lt;xml&amp;gt;&amp;lt;string id=&amp;quot;repair_kit_outfit_name&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;text&amp;gt;Ремонт комплект ДПК&amp;lt;/text&amp;gt;&lt;br /&gt;
    &amp;lt;/string&amp;gt;&lt;br /&gt;
        &amp;lt;string id=&amp;quot;enc_equipment_repair_kit_outfit&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;text&amp;gt;Новинка от Вани Болта - ремонт комплект ДПК для быстрой и эффективной починки костюмов. При его создании были учтены все недочёты и, следовательно, удалены. Благодаря улучшенным инструментам и деталям, а также облегченному весу весьма ценен. С его помощью можно увеличить состояние костюма до 100%, если он повреждён не больше 30%. Незаменимая вещь во время вылазки.&amp;lt;/text&amp;gt;&lt;br /&gt;
    &amp;lt;/string&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
Отлично, с предметом закончили. Теперь приступим к самому главному - скриптам. &lt;br /&gt;
===Работа со скриптами===&lt;br /&gt;
1.Вешаем функцию починки при использовании нашего ремонт комплекта. &lt;br /&gt;
Для этого в файле '''scripts\bind_stalker.script''' &lt;br /&gt;
находим функцию:&lt;br /&gt;
&lt;br /&gt;
''function actor_binder:net_destroy()'' и после строки &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.take_item_from_box, nil)&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
пишем: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.use_object, nil)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
И теперь находим функцию&lt;br /&gt;
&lt;br /&gt;
''function actor_binder:reinit()'' и после строки &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.take_item_from_box, self.take_item_from_box, self)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 пишем: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;self.object:set_callback(callback.use_object, self.repair_outfit_start, self)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Теперь в папке '''scripts''' создаёте свой скриптовой файл '''ваше_название_скрипта.script'''. Но! Название должно быть написано на английском языке и вместо пробелов должен стоять знак &amp;quot;_&amp;quot;. Опять открываем '''bind_stalker.script''' и в самый конец добавляем эту функцию: &lt;br /&gt;
&amp;lt;lua&amp;gt;function actor_binder:repair_outfit_start(obj, who)&lt;br /&gt;
    if obj then &lt;br /&gt;
        if obj:section() == &amp;quot;repair_kit_outfit&amp;quot; then --здесь указываем предмет, при использование которого будет срабатывать функция ниже&lt;br /&gt;
        ваше_название_скрипта.repair_outfit() --здесь указываем ссылку на функцию. Сначала скрипт, в котором она написана, потом название самой функции&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
2. Добавляем саму функцию ремонта. &lt;br /&gt;
Для этого в файле '''ваше_название_скрипта.script''' пишем: &lt;br /&gt;
&amp;lt;lua&amp;gt; -- Ремонт комплект для костюмов&lt;br /&gt;
function repair_outfit()&lt;br /&gt;
local item_s6 = db.actor:item_in_slot(6) --объявляем переменную (item_s6)&lt;br /&gt;
    if item_s6 ~= nil and item_s6:condition() &amp;gt;=0.30 and item_s6:condition() ~=1 then --когда костюм можно чинить (состояние больше или равно 30%)&lt;br /&gt;
        item_s6:set_condition(1)&lt;br /&gt;
        local snd_obj = xr_sound.get_safe_sound_object([[interface\inv_detach_addon]])&lt;br /&gt;
        snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0)&lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,0,255,0]Ремонт костюма завершен.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
    elseif item_s6 == nil then --когда костюма нет &lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,255,0,0]Ремонт невозможен: нужно одеть коcтюм.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
        alife():create(&amp;quot;repair_kit_outfit&amp;quot;, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())&lt;br /&gt;
    elseif item_s6:condition() &amp;lt;0.30 then --когда костюм нельзя чинить (состояние меньше 30%)&lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,255,0,0]Ремонт невозможен: костюм слишком сильно повреждён.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
        alife():create(&amp;quot;repair_kit_outfit&amp;quot;, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())&lt;br /&gt;
    elseif item_s6:condition() ==1 then --когда костюм в отличном состояние&lt;br /&gt;
        local news_text = &amp;quot;%c[255,160,160,160]Ремонт комплект:\\n%c[255,255,0,0]Ремонт невозможен: %c[255,0,255,0]костюм в отличном состояние.&amp;quot;&lt;br /&gt;
        db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
        alife():create(&amp;quot;repair_kit_outfit&amp;quot;, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())&lt;br /&gt;
    end&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
Всё! Теперь заходим в игру и наслаждаемся.&lt;br /&gt;
&lt;br /&gt;
===Небольшая заметка===&lt;br /&gt;
1. Чтобы сделать ремонт комплект для оружия проделываем тоже самое, только переименовываем всё вместо outfit на weapon и в функцие починки ставим слот 1 или 2 (оружейные слота) вместо 6 (слот костюмов).&lt;br /&gt;
Ещё нужно заменить переменную item_s6 например на item_s1 или item_s2.&lt;br /&gt;
&lt;br /&gt;
2. Число 0.30 - минимальное состояние костюма, следовательно его можно менять на любое вам угодное.&lt;br /&gt;
Простите за такое оформление, это моя вторая статья. В будущем, если будет больше свободного времени, постораюсь расписать все моменты подробно. Автор статьи: Бага.&lt;br /&gt;
&lt;br /&gt;
Данный метод создания ремонт комплектов не работает! (проверено)&lt;br /&gt;
ЗЫ: StalkerNew2012 этот скрипт подправил теперь всё работает :)&lt;br /&gt;
Ремонтный комплект без лишних сообщений просто съедается!:(&lt;br /&gt;
&lt;br /&gt;
3.Ремонт оружия и брони:&lt;br /&gt;
{| class=&amp;quot;collapsible collapsed&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;code lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
--     bind_stalker.script&lt;br /&gt;
function actor_binder:repair_outfit_start(obj, who)&lt;br /&gt;
    if obj then &lt;br /&gt;
        if obj:section() == &amp;quot;repair_kit_outfit&amp;quot; then&lt;br /&gt;
                remkomplekt.action_repair() &lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--     remkomplekt.script&lt;br /&gt;
function action_repair()&lt;br /&gt;
	-- эмулируем возврат ремкомплекта в инвентарь&lt;br /&gt;
	alife():create(&amp;quot;repair_kit_outfit&amp;quot;, vector(),0,0,0)&lt;br /&gt;
&lt;br /&gt;
	-- нет ли рядом врагов и монстров&lt;br /&gt;
	local apos = db.actor:position()&lt;br /&gt;
	for obj_id,_ in pairs (db.storage)do&lt;br /&gt;
		local obj = level.object_by_id(obj_id)&lt;br /&gt;
		if obj and (IsStalker(obj) or IsMonster(obj)) and obj:alive() &lt;br /&gt;
		and obj:relation(db.actor) == game_object.enemy and obj:position():distance_to(apos)&amp;lt;30 then&lt;br /&gt;
			send_tip(&amp;quot;Нельзя чинить костюм, когда враги слишком близко.&amp;quot;)&lt;br /&gt;
			return&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- пробуем чинить предмет в &lt;br /&gt;
	repair_item_in_slot(1) -- первом&lt;br /&gt;
	repair_item_in_slot(2) -- втором&lt;br /&gt;
	repair_item_in_slot(6) -- шестом слоте&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function repair_item_in_slot(num)&lt;br /&gt;
	local item = db.actor:item_in_slot(num)&lt;br /&gt;
	if item then&lt;br /&gt;
		local cond = item:condition()&lt;br /&gt;
		if cond &amp;lt; 0.3 then&lt;br /&gt;
			send_tip(&amp;quot;%c[255,255,0,0]Ремонт невозможен: предмет в слоте &amp;quot;..num.. &amp;quot; сильно повреждён.&amp;quot;)&lt;br /&gt;
		elseif cond &amp;gt; 0.95 then&lt;br /&gt;
			send_tip(&amp;quot;%c[255,255,0,0]Ремонт невозможен: %c[255,0,255,0]предмет в слоте &amp;quot;..num.. &amp;quot; в отличном состоянии.&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			item:set_condition(1)&lt;br /&gt;
			send_tip(&amp;quot;%c[255,0,255,0]Ремонт предмета в слоте &amp;quot;..num.. &amp;quot; завершен.&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		send_tip(&amp;quot;%c[255,255,0,0]В слоте &amp;quot;..num..&amp;quot; нет предмета для починки.&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function send_tip(news_text)&lt;br /&gt;
	db.actor:give_game_news(news_text, &amp;quot;ui\\ui_iconsTotal&amp;quot;, Frect():set(83,470,83,47), 1000, 5000)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
4. Для многоразового использования ремкомплекта можно не доспавнивать новый предмет при съедании, а в его конфиге указать для параметра '''eat_portions_num''' большое число.&lt;br /&gt;
&amp;lt;lua&amp;gt;eat_portions_num = 100500&amp;lt;/lua&amp;gt;&lt;br /&gt;
[[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=CoP._%D0%9E%D1%82%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_intro.</id>
		<title>CoP. Отключение intro.</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=CoP._%D0%9E%D1%82%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_intro."/>
				<updated>2014-01-04T23:23:10Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;1) Откройте любым текстовым редактором файл '''game_tutorials.xml''', что в папке ''gamedata\configs\ui'', распакованных ресурсов &amp;quot;Зова Припяти&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2) Найдите там строку '''&amp;lt;intro_logo&amp;gt;''', и замените весь код между:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;intro_logo&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/intro_logo&amp;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;
&amp;lt;global_wnd width=&amp;quot;1024&amp;quot; height=&amp;quot;768&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/global_wnd&amp;gt;&lt;br /&gt;
&amp;lt;item&amp;gt;&lt;br /&gt;
&amp;lt;main_wnd&amp;gt;&lt;br /&gt;
&amp;lt;/main_wnd&amp;gt;&lt;br /&gt;
&amp;lt;/item&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните изменения в файле.&amp;lt;br /&amp;gt;&lt;br /&gt;
Теперь никаких лого при запуске игры не будет.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3) В том же файле '''game_tutorials.xml''', найдите строку '''&amp;lt;intro_game&amp;gt;''', и замените весь код между:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&amp;lt;intro_game&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/intro_game&amp;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;
&amp;lt;global_wnd width=&amp;quot;1024&amp;quot; height=&amp;quot;768&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/global_wnd&amp;gt;&lt;br /&gt;
&amp;lt;item&amp;gt;&lt;br /&gt;
&amp;lt;main_wnd&amp;gt;&lt;br /&gt;
&amp;lt;/main_wnd&amp;gt;&lt;br /&gt;
&amp;lt;/item&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сохраните изменения в файле.&amp;lt;br /&amp;gt;&lt;br /&gt;
Теперь стартового ролика после начала новой игры не будет.&lt;br /&gt;
&lt;br /&gt;
= Авторы =&lt;br /&gt;
&lt;br /&gt;
Статью создал: [[aka_sektor]]&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= P.S. =&lt;br /&gt;
&lt;br /&gt;
Параметр запуска '''-nointro''' на ЗП не работает, вот пришлось разобрался с этим раз и навсегда.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Категория:Конфигурационные файлы]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=%D0%9C%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80_%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2</id>
		<title>Менеджер сигналов</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%9C%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80_%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2"/>
				<updated>2013-12-25T17:13:16Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: /* Подписывание функционального объекта */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Скрипты]]&lt;br /&gt;
&lt;br /&gt;
= Система сигналов в стиле Boost.Signals или делегатов C# =&lt;br /&gt;
&lt;br /&gt;
== Введение ==&lt;br /&gt;
&lt;br /&gt;
Как мы все знаем, давно распространена практика &amp;quot;навешивания&amp;quot; всевозможных действий на разные вызовы биндера актора: виртуальные функции, такие как '''net_spawn''' и '''update''', а также всевозможные колбеки. Как правило, это делается вставкой строки в один из методов биндера в модуле '''bind_stalker.script''' (в котором как раз и находится класс '''actor_binder'''). Для случая метода '''update''' это может выглядеть примерно так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;function actor_binder:update(delta)&lt;br /&gt;
    object_binder.update(self, delta)&lt;br /&gt;
    ...&lt;br /&gt;
    my_module.update(delta)&lt;br /&gt;
    ...&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
здесь '''my_module.update(delta)''' - эта наша врезка. Вызовы из разных методов и колбеков используются для разных целей. Тот-же апдейт широко (даже слишком широко) используется для всевозможных периодических проверок. Данный подход имеет несколько недостатков.&lt;br /&gt;
&lt;br /&gt;
=== Недостатки существующих подходов ===&lt;br /&gt;
&lt;br /&gt;
1. Добавление очередного сервиса требует внесения изменений в модуль '''bind_stalker.script'''. Это не плохо само по себе, но постепенно приводит к замусориванию и разрастанию этого модуля, что не всем нравится. Кроме того, внесение изменений в существующий код потенциально чревато ошибками.&lt;br /&gt;
&lt;br /&gt;
2. Поскольку каждый новый добавляемый сервис пишется по-разному, то возникает изрядный разнобой как в именовании вызовов, так и в передаваемых аргументах. Это опять же не плохо само по себе, но затрудняет сопровождение кода.&lt;br /&gt;
&lt;br /&gt;
3. Усложнение отладки. Давно известна проблема подвисания различных вызовов биндера в случае возникновения в нем исключения движка. При таких ситуациях вызов тихо перестаёт вызываться с неопределёнными последствиями. Впрочем, неопределённость проявляется только в виде неочевидности и немгновенности эффектов. С точки зрения глобальных последствий всё достаточно определённо. Это почти всегда фатально для дальнейшего продолжения игры, приводит к порче сейвов и крайне мутным и трудноотлаживаемым глюкам.&lt;br /&gt;
&lt;br /&gt;
Поэтому крайне важно отловить такую ситуацию сразу и попросту остановить игру. Желательно также получить информацию о том, какой именно вызов вызвал зависание. Для этой цели обычно делают систему со счётчиком и флажками, которая позволяет понять, что вызов не завершился и по крайней мере понять, в какой именно момент он подвис. Однако, при каждом внесении изменения требуется подлаживать также и отладочные вызовы, что по крайней мере усложняет процесс и раздувает код. Кроме того, при этом сложно отлавливать ситуацию вложенных вызовов.&lt;br /&gt;
&lt;br /&gt;
4. Ряд используемых в модостроении техник сложно назвать как-то иначе, кроме как уродливые. Одна из таких техник, широко используемая в соединении с сервисами биндера - это использование колбека на использование инвентарного предмета. Сложности начинаются тогда, когда на колбек использования повешено множество вызовов. Естественно подразумевается, что реально что-то делать с использованным предметом должен только один из вызовов, хотя технически что-то делает каждый, в частности проверяет предмет и выясняет, должен ли данный вызов что-то по этому поводу делать.&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;
Об этом много написано (см. boost.signals, делегаты C#, слоты и сигналы Qt и т.п.), но не лишним будет и повториться. Попробую изложить идею на конкретном примере.&lt;br /&gt;
&lt;br /&gt;
Пусть у нас есть ситуация, когда актор использует некий предмет в инвентаре. Мы имеем движковый колбек, который срабатывает при использовании предмета, расположен в модуле '''bind_stalker.script'''&lt;br /&gt;
&amp;lt;lua&amp;gt;function actor_binder:use_inventory_item(obj)&lt;br /&gt;
    ...&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
Как было описано в предыдущей части, в модостроении этот колбек используется для многочисленных действий. К примеру, интерфейс использования спального мешка, ремкомплекта и вообще всевозможных устройств, активируемых предметов из инвентаря, дополнительные эффекты от съедаемых предметов и медикаментов и т.п. Соответственно, вызов соответствующих обработок может выглядеть примерно так:&lt;br /&gt;
&amp;lt;lua&amp;gt;function actor_binder:use_inventory_item(obj)&lt;br /&gt;
    sleep_manager.on_use(obj) -- активация спального мешка&lt;br /&gt;
    remkit.use_object(obj) -- активация ремкита&lt;br /&gt;
    healing.use_item(obj) -- дополнительные эффекты от препаратов&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
Недостатки этого подхода были подробно описаны ранее. &lt;br /&gt;
&lt;br /&gt;
Что здесь событие? Событие - это факт использования актором предмета. Из примера видно, что событие одно, а действий, которые происходят по событию, может быть много. А может и не быть вовсе, хотя событие происходит вне зависимости от того, связаны ли с ним действия. Отсюда вытекает простая идея отделить событие от обработчиков события. Т.е. я хотел бы иметь в коде что-то вроде такого:&lt;br /&gt;
&amp;lt;lua&amp;gt;function actor_binder:use_inventory_item(obj)&lt;br /&gt;
    генерировать_событие_использования_предмета(obj)&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
что приводило бы к срабатыванию функций '''sleep_manager.on_use(obj)''', '''remkit.use_object(obj)''' и '''healing.use_item(obj)'''. Естественно, что это потребует неких усилий: нужен некий промежуточный код, который будет хранить список функций, связанных с событием, который будет при активации события вызывать эти функции одна за другой, передавая в них один и тот-же аргумент; нужен сервисный код, который позволит регистрировать функции-обработчики и связывать их с конкретным событием, а также отвязывать. В частности, используя этот сервисный код, надо предварительно выполнить действия такого рода:&lt;br /&gt;
&amp;lt;lua&amp;gt;связать_функцию_с_событием_использования_предмета(sleep_manager.on_use)&lt;br /&gt;
связать_функцию_с_событием_использования_предмета(remkit.use_object)&lt;br /&gt;
связать_функцию_с_событием_использования_предмета(healing.use_item)&amp;lt;/lua&amp;gt;&lt;br /&gt;
чтобы система событий знала, что надо вызвать эти три функции. &lt;br /&gt;
&lt;br /&gt;
Немного о терминологии. В разных системах и языках для разных частей этого процесса используются разные названия. Событие (event) может также называться сигналом (signal). Функции-обработчики могут также называться слотами (slot), подписчиками (subscriber), делегатами (delegate), колбеками (callback). Соответственно процесс связывания сигнала и обработчика тоже может называться по-разному: (un)subscribe, (dis)connect, add/remove и прочие слова, которые так или иначе могут означать &amp;quot;связать&amp;quot;, &amp;quot;добавить&amp;quot;, &amp;quot;подписать&amp;quot;, &amp;quot;назначить&amp;quot; и т.п. В предлагаемой системе я использую термины &amp;quot;сигнал&amp;quot;, &amp;quot;слот&amp;quot;, &amp;quot;подписать/отписать&amp;quot;. На самом деле рекомендую не делать из этого священную корову, главное понимать смысл.&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;quot;спальный мешок&amp;quot; адресовано только обработчику менеджера сна. При этом, зачастую объект удаляется его обработчиком, что приводит к тому, что последующие обработчики имеют дело с объектом в стадии удаления (клиентский объект ещё есть, а серверного уже нет), что запросто может приводить к багам или дополнительному громоздкому коду, который должен эту ситуацию проверять. При использовании же системы сигналов у обработчиков есть возможность завершить цепочку вызовов на себе, и предотвратить вызов оставшихся в цепочке обработчиков. Для этого обработчик должен вернуть '''true''' (не вернуть ничего или вернуть '''false''' будет означать разрешение продолжить обработку). Кроме того, для некоторых вызовов сам менеджер производит проверку существования объекта, и если он уже удалён, то дальнейшие вызовы не будет сделаны вообще. Для обработчиков нажатий клавиатуры этот подход позволяет снизить нагрузку на процессор.&lt;br /&gt;
#* Есть возможность распределения нагрузки в виде низкоприоритетных (или очерёдных колбеков). В основном это имеет смысл только для использования вместе с сигналом update, тем не менее весьма полезно. Без такой фишки приходится делать это вручную со счётчиком, километровым кодом с if-ами и т.п.&lt;br /&gt;
# Обработчики можно не только подписывать на сигналы, но и отписывать. Это позволяет делать разнообразные динамические компоненты, которые &amp;quot;цепляются&amp;quot; к нужному сигналу и отсоединяются по мере необходимости. Это упрощает разработку, поскольку позволяет избежать громоздкого кода, который при отсутствии такой возможности проверял бы необходимость вызова, и, как следствие, повышает надёжность системы. Это активно используется, к примеру, в системе таймеров.&lt;br /&gt;
# На события можно подписывать не только обычные функции, но и методы классов. Это фишка конкретно этой реализации, но на мой взгляд полезная. Используется при реализации тех-же таймеров.&lt;br /&gt;
# В принципе возможны трюки с эмуляцией сигнала. Т.е. к примеру сигнал update биндера актора нормально вызывается из биндера актора, но никто не мешает принудительно вызывать его откуда-то ещё, что вызовет срабатывание всех подписанных на этот сигнал обработчиков. Я бы не рекомендовал использовать это без лишней нужды, но тем не менее возможность такая есть.&lt;br /&gt;
# Автоподключение модулей. Идея простая: пишется модуль с функциями-обработчиками каких-то событий, имя модуля должно следовать неким правилам для его распознавания менеджером, и также модуль должен содержать специальную переменную-метку и функцию, которая автоматически будет выполнена. В этой функции выполняется подписка на события функций из этого модуля. Используя эту технику можно написать модуль с колбеками, не изменив вообще ни строки ни в одном другом модуле. Всё, что надо сделать для его использования, просто поместить файл в папку scripts. Например, можно написать модуль, который выводит на худ информацию об объекте под прицелом по нажатию сочетания клавиш (нужны естественно колбеки на клавиши), или удаляет объект под прицелом, или меняет его свойства, или телепортирует актора на три метра вперёд, или открывает окно тестового спавна и т.п. Не нужен модуль, просто убрал его из папки со скриптами. Это всё очень удобно для отладочных модулей. Для использования в штатных компонентах не рекомендуется, поскольку по ряду причин для автоподписываемых модулей ослаблены проверки корректности.&lt;br /&gt;
&lt;br /&gt;
== Использование менеджера ==&lt;br /&gt;
&lt;br /&gt;
=== Общие сведения ===&lt;br /&gt;
&lt;br /&gt;
Код менеджера сигналов находится в двух модулях:&lt;br /&gt;
&lt;br /&gt;
'''ogse_signals.script''' - собственно менеджер сигналов&lt;br /&gt;
&lt;br /&gt;
'''ogse_signals_addons_list.script''' - список подписываемых модулей&lt;br /&gt;
&lt;br /&gt;
Глобальный объект менеджера сигналов существует в единственном экземпляре. Поучить его можно функцией '''ogse_signals.get_mgr()'''. Объект менеджера используется для всех дальнейших операций: подписки/отписки функций и вызова сигналов.&lt;br /&gt;
&lt;br /&gt;
Для подписки используется метод менеджера '''subscribe(slot_descriptor)''', для отписки - метод '''unsubscribe(slot_descriptor)''', для генерации сигнала - метод '''call(&amp;quot;signal_name&amp;quot;, &amp;lt;список аргументов&amp;gt;)'''&lt;br /&gt;
'''slot_descriptor''' - это таблица, имеющая вид:&lt;br /&gt;
&amp;lt;lua&amp;gt;slot_descriptor = {signal = &amp;quot;signal_name&amp;quot;, fun = function_or_class_member, self = object_reference_or_nil, queued = true}&amp;lt;/lua&amp;gt;&lt;br /&gt;
Если необходимо отписать функцию от события, то надо сохранить этот дескриптор и позднее использовать его в функции '''unsubscribe'''. Для иллюстрации всего этого далее приводятся несколько конкретных примеров использования.&lt;br /&gt;
&lt;br /&gt;
=== Подписка глобальной функции ===&lt;br /&gt;
Допустим, имеется модуль '''some_module.script''', а в нём функция&lt;br /&gt;
&amp;lt;lua&amp;gt;function some_function(arg1, arg2)&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда я могу выполнить подписку этой функции на сигнал &amp;quot;'''some_signal'''&amp;quot; таким образом:&lt;br /&gt;
&amp;lt;lua&amp;gt;local slot_desc = {signal = &amp;quot;some_signal&amp;quot;, fun = some_module.some_function} -- дескриптор слота&lt;br /&gt;
ogse_signals.get_mgr():subscribe(slot_desc) -- подписали слот&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С этого момента при генерации сигнала &amp;quot;'''some_signal'''&amp;quot; будет вызываться функция '''some_module.some_function'''. Генерация сигнала (а по-простому вызов) осуществляется так:&lt;br /&gt;
&amp;lt;lua&amp;gt;ogse_signals.get_mgr():call(&amp;quot;some_signal&amp;quot;, arg1, arg2) -- вызов сигнала. В каждый обработчик будут переданы аргументы arg1, arg2&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для отписки этой функции делаем так (при условии, что мы сохранили дескриптор слота '''slot_desc'''):&lt;br /&gt;
&amp;lt;lua&amp;gt;ogse_signals.get_mgr():unsubscribe(slot_desc) -- отписали функцию&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Замечание: модули, в котором находится функция '''some_module.some_function''', из которого осуществляется подписка и отписка функции и откуда вызывается сигнал, могут быть совершенно разными. Хотя чаще всего получается так, что первые три - это один модуль, а откуда вызывается сигнал - другой.&lt;br /&gt;
&lt;br /&gt;
=== Подписка глобальной функции в низкоприоритетную очередь ===&lt;br /&gt;
&lt;br /&gt;
Здесь рассмотрим пример подписки функции на очерЁдное выполнение или оно же выполнение с низким приоритетом. Смысл очерёдности в том, что за каждый вызов сигнала выполняются не все подписанные функции, а только одна, в следующий раз следующая за ней в очереди и т.д. по кругу. В основном это имеет смысл только для события &amp;quot;'''update'''&amp;quot;, которое вызывается из функции апдейта биндера актора. Используя эту возможность, можно распределить нагрузку между последовательными апдейтами за счёт снижения частоты вызовов каждого конкретного подписчика. Обращаю внимание, что при этом частота вызовов будет зависеть от количества подписчиков - чем их больше, тем реже они вызываются. Естественно, не для любых операций это годится, а только для тех, где важен факт срабатывания по принципу &amp;quot;пусть сработает хоть когда-нибудь&amp;quot;. Если важна скорость реакции, то всегда остаётся возможность подписать на то же событие апдейта с высоким приоритетом. Технически эта фишка работает с использованием второй очереди.&lt;br /&gt;
&lt;br /&gt;
В примере ниже подразумевается, что подписываемая функция и код подписывания/отписывания находятся в одном модуле. Таким образом, я могу избежать указания имени модуля. Более того, я могу использовать системную ссылку this, которая означает &amp;quot;этот модуль&amp;quot;, чтобы избежать потенциальных конфликтов имён (вдруг среди глобальных имён встречается '''on_update''').&lt;br /&gt;
&amp;lt;lua&amp;gt;function on_update() -- функция-обработчик события низкоприоритетного обновления&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
ogse_signals.get_mgr():subscribe({signal = &amp;quot;on_update&amp;quot;, fun = this.on_update, queued = true}) -- подписали&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
В качестве дополнительного замечания. В этом примере мы не хотим отписываться от сигнала. Это вполне нормальная ситуация в особенности для глобальных функций типа обработчиков периодического апдейта. В этом случае нет необходимости сохранять дескриптор слота и синтаксис в целом упрощается.&lt;br /&gt;
&lt;br /&gt;
=== Подписка метода класса ===&lt;br /&gt;
&lt;br /&gt;
Имеется возможность подписать на событие метод класса. Напоминаю, что при вызове метода класса в него передаётся скрытый аргумент self, соответственно, при регистрации обработчика надо в дескрипторе указать дополнительный параметр со ссылкой на объект класса. Пример ниже показывает идею, на которой основана работы системы таймеров:&lt;br /&gt;
&amp;lt;lua&amp;gt;class &amp;quot;simple_timer&amp;quot;&lt;br /&gt;
function simple_timer:__init() -- конструктор класса&lt;br /&gt;
    self.slot_desc = {signal = &amp;quot;on_update&amp;quot;, self = self, fun = self.on_update}&lt;br /&gt;
    self.sm = ogse_signals.get_mgr()&lt;br /&gt;
    sm:subscribe(self.slot_desc)&lt;br /&gt;
end&lt;br /&gt;
function simple_timer:on_update() -- функция периодической проверки некоего условия&lt;br /&gt;
    if &amp;lt;выполнилось некое условие&amp;gt; then&lt;br /&gt;
        self:on_finish()&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
function simple_timer:on_finish() -- отписка и завершение выполнения&lt;br /&gt;
    sm:unsubscribe(self.slot_desc)&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В данном примере объект класса при своём создании сам регистрирует один из своих методов на событие периодического апдейта. Далее, в этой функции проверяется некое условие. Когда это условие выполняется, то вызывается функция завершения работы, которая, кроме выполнения разной полезной нагрузки, также разрегистрирует класс в менеджере сигналов. Таким образом, для начала работы этого простого таймера достаточно просто его создать.&lt;br /&gt;
&amp;lt;lua&amp;gt;simple_timer()&amp;lt;/lua&amp;gt;&lt;br /&gt;
и он автоматически начнёт работу. Более того, нет даже необходимости нигде хранить ссылку на этот объект, поскольку она хранится в менеджере сигналов (в поле '''self'''). При отписывании же, после окончании работы таймера, эта ссылка удаляется и вместе с ней сборщиком мусора удаляется и объект таймера.&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;quot;'''call'''&amp;quot;, то такой класс, с одной стороны, можно подписать как глобальную функцию, а с другой - он может хранить состояние, в отличие от простой функции.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;class &amp;quot;some_luabind_class&amp;quot;&lt;br /&gt;
function some_luabind_class:__init()&lt;br /&gt;
	local mt = getmetatable(self)&lt;br /&gt;
	mt.__call = self.method_to_call&lt;br /&gt;
end&lt;br /&gt;
function some_luabind_class:method_to_call()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local slot_desc = {signal = &amp;quot;signal_name&amp;quot;, fun = some_luabind_class()}&lt;br /&gt;
ogse_signals.get_mgr():subscribe(slot_desc) -- подписали в высокоприоритетную очередь&lt;br /&gt;
--...&lt;br /&gt;
ogse_signals.get_mgr():unsubscribe(slot_desc) -- отписали&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функциональный класс на таблице строится немного сложнее:&lt;br /&gt;
&amp;lt;lua&amp;gt;local t = {}&lt;br /&gt;
function t:method_to_call()&lt;br /&gt;
end&lt;br /&gt;
local mt = {}&lt;br /&gt;
mt.__call = t.method_to_call&lt;br /&gt;
setmetatable(t, mt)&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;
1. Вписать имя модуля без расширения в таблицу '''addons''' в файле '''ogse_signals_addons_list.script'''. Допустим, у меня есть модуль '''my_module.script''', тогда я напишу так:&amp;lt;lua&amp;gt;addons = {&lt;br /&gt;
	&amp;quot;my_module&amp;quot;,&lt;br /&gt;
}&amp;lt;/lua&amp;gt;Примечание: запятая после последнего элемента массива допускается синтаксисом '''Lua'''.&lt;br /&gt;
&lt;br /&gt;
2. В самом модуле '''my_module.script''' должна иметься глобальная функция '''attach(sm)''' с единственным аргументом. Эта функция будет вызвана автоматически при старте игры, а аргумент - это ссылка на менеджер сигналов. Функция может выглядеть примерно так:&lt;br /&gt;
&amp;lt;lua&amp;gt;function attach(sm)&lt;br /&gt;
	sm:subscribe({signal = &amp;quot;on_spawn&amp;quot;, fun = this.on_spawn})&lt;br /&gt;
	sm:subscribe({signal = &amp;quot;on_use&amp;quot;,   fun = this.on_item_use})&lt;br /&gt;
	sm:subscribe({signal = &amp;quot;on_update&amp;quot;,fun = this.on_update, queued = true})&lt;br /&gt;
	sm:subscribe({signal = &amp;quot;on_save&amp;quot;,  fun = this.on_save})&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
т.е. её задача - явно подписать на нужные сигналы обработчики из этого модуля. &lt;br /&gt;
&lt;br /&gt;
Этот подход позволяет выполнить интеграцию скриптовой части минимода с минимальным остальных скриптов. По сути, извне модуля меняется только таблица в файле '''ogse_signals_addons_list.script''' - туда добавляется одна строка. Разумеется, соответствующие сигналы уже должны быть заведены в разных колбеках мода, но для большинства стандартных сигналов (типа колбеков биндера актора update, spawn, use_item и т.п.) это обычно уже сделано. Также, соглашения о вызовах обработчиков из подключаемого модуля должны следовать соглашениям о вызовах соответствующих сигналов.&lt;br /&gt;
&lt;br /&gt;
=== Авторегистрация модуля ===&lt;br /&gt;
&lt;br /&gt;
Имеется возможность подключать модуль без вписывания его в файл '''ogse_signals_addons_list.script'''. Для этого он должен отвечать двум дополнительным требованиям:&lt;br /&gt;
# Имя модуля должно начинаться с &amp;quot;ogse_&amp;quot;&lt;br /&gt;
# Модуль должен содержать глобальную переменную '''auto_attach''', установленную в '''true'''.&lt;br /&gt;
&lt;br /&gt;
Менеджер сигналов сканирует каталог скриптов, находит подходящие по имени, проверяет наличие переменной '''auto_attach''' и её значение, наличие функции '''attach''', и если всё это присутствует, то пытается зарегистрировать модуль (т.е. собственно выполнить функцию '''attach''').&lt;br /&gt;
&lt;br /&gt;
Данный способ, несмотря на внешнее удобство, не рекомендуется использовать для &amp;quot;серьёзных&amp;quot; компонент. Дело в том, что если в модуле автоподключаемого модуля имеется синтаксическая ошибка, то при автоподключении он будет просто проигнорирован, что снижает надёжность системы. Данную возможность предпочтительно использовать для отладочных плагинов.&lt;br /&gt;
&lt;br /&gt;
В качестве лирического отступления. Данная фишка на заре создания этой системы была основным функционалом, ради которого я эту систему и делал. Мне нужна была возможность создавать отладочные модули, которые можно было подключать/отключать просто перенося файл скрипта в папку '''scripts'''. При наличии движковых колбеков на нажатия клавиш получается очень удобная система. К примеру, есть наработки по отладочным модулям, которые позволяют снимать информацию о разных объектах вблизи актора и выводить её в лог или на экран по нажатию нужных сочетаний. С другой стороны я не включаю эти модули в релиз для тестеров, и это не требует изменения ни одной строки кода.&lt;br /&gt;
&lt;br /&gt;
==Авторы==&lt;br /&gt;
Статья создана: [[Участник:malandrinus|malandrinus]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D0%B7%D0%B0%D1%81%D0%BF%D0%B0%D0%B2%D0%BD%D0%B8%D1%82%D1%8C_%D1%84%D0%B8%D0%B7%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82</id>
		<title>Как заспавнить физический объект</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D0%B7%D0%B0%D1%81%D0%BF%D0%B0%D0%B2%D0%BD%D0%B8%D1%82%D1%8C_%D1%84%D0%B8%D0%B7%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82"/>
				<updated>2013-08-11T15:11:08Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: оформление&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этой статье вы узнаете как создать спавнящийся физический объект.&lt;br /&gt;
&amp;lt;br&amp;gt;Выбираем нужную модель из папки '''gamedata\meshes'''.&lt;br /&gt;
&amp;lt;br&amp;gt;Создаем в файле '''configs\misc\items.ltx''' (или своем конфиге) новую секцию, которая будет выглядеть так:&lt;br /&gt;
&amp;lt;ini&amp;gt;&lt;br /&gt;
[vedro]:identity_immunities ;vedro — название спавн-секции предмета (Потом мы будем через него спавнить). Пишется латиницей.&lt;br /&gt;
$spawn = &amp;quot;dynamic_objects\el_tehnika\komp_klava&amp;quot; ;путь спавн-секции в дереве спавн-объектов в СДК (если не пользуетесь СДК, то особой важности не имеет).&lt;br /&gt;
visual = objects\dynamics\decor\vedro_01.ogf ;Путь до модели физического объекта из папки meshes.&lt;br /&gt;
cform  = skeleton ; Движковые классы (не изменяем)&lt;br /&gt;
class  = P_SKELET&lt;br /&gt;
remove_time = 4 ;Время через которое объект исчезнет. Число до от 0 до 9999, если поставить 9999 то он просуществует очень долго&amp;lt;/ini&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Второй вариант:&lt;br /&gt;
&amp;lt;ini&amp;gt;[vedro]:identity_immunities ;vedro — название спавн-секции предмета (Потом мы будем через него спавнить). Пишется латиницей.&lt;br /&gt;
$spawn = &amp;quot;physics\object&amp;quot; ;путь спавн-секции в дереве спавн-объектов в СДК (если не пользуетесь СДК, то особой важности не имеет).&lt;br /&gt;
visual = objects\dynamics\decor\vedro_01.ogf ;Путь до модели физического объекта из папки meshes.&lt;br /&gt;
class  = O_PHYS_S&lt;br /&gt;
remove_time = 60000 ;Время через которое объект исчезнет. Число до от 0 до 9999, если поставить 9999 то он просуществует очень долго.&lt;br /&gt;
&amp;lt;/ini&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;Сохраняем файл.&lt;br /&gt;
&lt;br /&gt;
Далее уже стандартно — создаем в скриптовом файле функцию типа&lt;br /&gt;
&amp;lt;lua&amp;gt;function spawn () &lt;br /&gt;
alife():create(&amp;quot;vedro&amp;quot;,vector():set(координаты X,Y,Z),level_vertex_id,game_vertex_id)&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
И вызываем эту функцию через диалог или другой скрипт.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Автор — [[Участник:KitkaT.Net|KitkaT.Net]] 12:45, 27 сентября 2010 (UTC)&amp;lt;br /&amp;gt;&lt;br /&gt;
Редактирование — [[Участник:Pihan13|Pihan13]] 16:04, 27 сентября 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
[[Категория:Конфигурационные файлы]][[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BD%D0%BE%D0%B2%D1%8B%D1%85_%D0%B2%D0%B5%D1%82%D0%BE%D0%BA_%D0%B4%D0%B8%D0%B0%D0%BB%D0%BE%D0%B3%D0%BE%D0%B2</id>
		<title>SoC. Создание новых веток диалогов</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=SoC._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BD%D0%BE%D0%B2%D1%8B%D1%85_%D0%B2%D0%B5%D1%82%D0%BE%D0%BA_%D0%B4%D0%B8%D0%B0%D0%BB%D0%BE%D0%B3%D0%BE%D0%B2"/>
				<updated>2013-08-03T16:24:56Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: /* Дополение от XiaNi */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;lt;center&amp;gt;Создание новых веток диалогов.&amp;lt;/center&amp;gt;==&lt;br /&gt;
===Теория:===&lt;br /&gt;
&lt;br /&gt;
(Если где то чего то напутал исправляйте, многое писал интуитивно)&lt;br /&gt;
&lt;br /&gt;
'''1)''' Списки веток диалогов содержатся в файлах вида '''gamedata/config/gameplay/character_desc_*.xml''' &lt;br /&gt;
&lt;br /&gt;
Это например: &lt;br /&gt;
&amp;lt;br&amp;gt;'''character_desc_zombied.xml'''&lt;br /&gt;
&amp;lt;br&amp;gt;'''character_desc_stalker.xml'''&lt;br /&gt;
&amp;lt;br&amp;gt;'''character_desc_garbage.xml'''&lt;br /&gt;
и т. д.&lt;br /&gt;
&lt;br /&gt;
Файлы '''character_desc_*.xml''' можно сравнить со стволом дерева диалогов.&lt;br /&gt;
В них перечисляется названия прикрепляемых к НПС веток диалогов.&lt;br /&gt;
&lt;br /&gt;
Например, вот список веток диалогов с Сидоровичем, взятый из файла '''character_desc_escape.xml'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;		&amp;lt;start_dialog&amp;gt;escape_trader_start_dialog&amp;lt;/start_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;escape_trader_talk_info&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;escape_trader_jobs&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;tm_trader_dialog&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;tm_trader_reward&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;escape_trader_done_blockpost_box&amp;lt;/actor_dialog&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В свою очередь, каждая ветка диалога также может ветвится.&lt;br /&gt;
&lt;br /&gt;
'''2)''' Ветвление веток прописывается уже в других файлах.&lt;br /&gt;
Например, ветвление веток диалога с Сидоровичем содержится в файле '''gamedata/config/gameplay/dialogs_escape.xml'''&lt;br /&gt;
Возьмем оттуда, к примеру, ветвление ветки ''escape_trader_jobs''&lt;br /&gt;
&lt;br /&gt;
(ветвление веток обычно очень большое, поэтому я приведу только часть):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&amp;lt;dialog id=&amp;quot;escape_trader_talk_info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;escape_dialog.trader_has_talk_info_wr&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;has_info&amp;gt;tutorial_end&amp;lt;/has_info&amp;gt;&lt;br /&gt;
        &amp;lt;phrase_list&amp;gt;&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;escape_trader_talk_info_1&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;100&amp;lt;/next&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;99&amp;lt;/next&amp;gt;&lt;br /&gt;
	      &amp;lt;next&amp;gt;9995&amp;lt;/next&amp;gt;&lt;br /&gt;
            &amp;lt;/phrase&amp;gt;&lt;br /&gt;
&lt;br /&gt;
              …&lt;br /&gt;
              …&lt;br /&gt;
              …&lt;br /&gt;
            &amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;escape_trader_talk_info_0&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_list&amp;gt;&lt;br /&gt;
&amp;lt;/dialog&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь ''&amp;lt;precondition&amp;gt;…&amp;lt;/precondition&amp;gt;'' - это скриптовая проверка выполнения условия. Ветка появится в диалоге, только если условие выполняется.&lt;br /&gt;
Конкретно ''&amp;lt;precondition&amp;gt;escape_dialog.trader_has_talk_info_wr&amp;lt;/precondition&amp;gt;'' из ветки  ''escape_trader_talk_info'' есть обращение к функции ''trader_has_talk_info_wr'', находящейся в файле-скрипте '''gamedata/scripts/escape_dialog.script'''.&lt;br /&gt;
&lt;br /&gt;
Функция выглядит так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;function trader_has_talk_info_wr( trader, actor )&lt;br /&gt;
	return true&lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''&amp;lt;precondition&amp;gt;escape_dialog.trader_has_talk_info_wr&amp;lt;/precondition&amp;gt;'' в принципе выполняется всегда, т. к. функция всегда возвращает истbну и ''&amp;lt;dialog id=&amp;quot;escape_trader_talk_info&amp;quot;&amp;gt;'' пропускается в списке реплик.&lt;br /&gt;
Но для конкретной ветки может быть несколько ''precondition'' и других условий.&lt;br /&gt;
&lt;br /&gt;
Далее.&lt;br /&gt;
&amp;lt;br&amp;gt;''&amp;lt;has_info&amp;gt;tutorial_end&amp;lt;/has_info&amp;gt;'' - это также своего рода проверка условия. В данном случае проверка на то, закончена стадия ''tutorial'а'' или нет. Т. е. ветка будет допущена в список реплик, если стадия ''tutorial'а'' закончена.&lt;br /&gt;
более детально об этом в конце данной статьи. При окончании ''tutorial'а'' выдается инфопорция ''tutorial_end'', которая и является стержнем данной проверки.  &lt;br /&gt;
&lt;br /&gt;
А далее идут конкретные фразы содержащие ссылки на вытекающие фразы&lt;br /&gt;
&lt;br /&gt;
Фраза:        &lt;br /&gt;
&amp;lt;xml&amp;gt;&amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;escape_trader_talk_info_0&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;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
это основа ветки ''escape_trader_talk_inf''o. &lt;br /&gt;
Вообще, в любой основной ветке любого диалога фраза ''&amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;'' будет основой, из которой далее будет все вытекать.&lt;br /&gt;
&lt;br /&gt;
''&amp;lt;next&amp;gt;1&amp;lt;/next&amp;gt;'' - это ссылка на вытекающую фразу ''&amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;'':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;       &amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;text&amp;gt;escape_trader_talk_info_1&amp;lt;/text&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;100&amp;lt;/next&amp;gt;&lt;br /&gt;
                &amp;lt;next&amp;gt;99&amp;lt;/next&amp;gt;&lt;br /&gt;
	    &amp;lt;next&amp;gt;9995&amp;lt;/next&amp;gt;&lt;br /&gt;
        &amp;lt;/phrase&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В свою очередь   ''&amp;lt;next&amp;gt;100&amp;lt;/next&amp;gt;'', ''&amp;lt;next&amp;gt;99&amp;lt;/next&amp;gt;'', ''&amp;lt;next&amp;gt;9995&amp;lt;/next&amp;gt;'' - это ссылки на фразы веточки растущие из фразы ''&amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;''.  &lt;br /&gt;
&lt;br /&gt;
'''3)''' Текст каждой фразы содержится уже в четвертом файле. Для диалога с Сидоровичем тексты лежат в файле '''gamedata/config/text/rus/stable_dialogs_escape.xml'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;	&amp;lt;string id=&amp;quot;escape_trader_talk_info_0&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;text&amp;gt;Есть несколько вопросов.&amp;lt;/text&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&lt;br /&gt;
	&amp;lt;string id=&amp;quot;escape_trader_talk_info_1&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;text&amp;gt;Спрашивай, только я ведь всего не знаю. Сам понимаешь, сижу тут целыми днями, а жизнь - она вся там, снаружи, в Зоне. Могу рассказать о Зоне вообще, а немного могу о ближайших окрестностях, где сам ходил.&amp;lt;/text&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти строки содержат тексты для фраз ''&amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;'' и ''&amp;lt;phrase id=&amp;quot;1&amp;quot;&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
Итого диалоги разложены по 4 а то и более файлам.&lt;br /&gt;
&lt;br /&gt;
Да кстати, путь по веткам может быть зацикленным если того требует диалог. Например так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;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;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;…&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;text&amp;gt;…&amp;lt;/text&amp;gt;&lt;br /&gt;
            &amp;lt;next&amp;gt;1&amp;lt;/next&amp;gt;  - Это возврат к фразе 1. (зацикливание)  &lt;br /&gt;
            &amp;lt;next&amp;gt;111&amp;lt;/next&amp;gt;&lt;br /&gt;
 &amp;lt;/phrase&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Практика:===&lt;br /&gt;
Добавим в диалог с Сидоровичем ветку своего собственного изготовления.Например такую: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Меченый: Сидрыч а почему это у тебя зеленые человечки, что по столу бегают, такие худые? &lt;br /&gt;
Сидорович: Чего?!!&lt;br /&gt;
Меченый: Ты их совсем, совсем не кормишь?&lt;br /&gt;
Сидорович: В следующий раз, как пойдешь в зону, бери-ка  вместо водяры побольше антирада. А то мало что таким перегаром дышишь, уже до зеленых человечков долечился… Шутник.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для этого:&lt;br /&gt;
&lt;br /&gt;
'''1)''' В файле gamedata/config/gameplay/character_desc_escape.xml в конце списка веток для  trader_а  суем свою ветку с произвольным названием. (например   &amp;lt;actor_dialog&amp;gt;escape_trader_letat_gusi&amp;lt;/actor_dialog&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Т.е у нас получится так&lt;br /&gt;
&amp;lt;xml&amp;gt;		…&lt;br /&gt;
	&amp;lt;specific_character id=&amp;quot;escape_trader&amp;quot; no_random = &amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
		…&lt;br /&gt;
		…&lt;br /&gt;
		…&lt;br /&gt;
		&amp;lt;start_dialog&amp;gt;escape_trader_start_dialog&amp;lt;/start_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;escape_trader_talk_info&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;escape_trader_jobs&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;tm_trader_dialog&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;tm_trader_reward&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
		&amp;lt;actor_dialog&amp;gt;escape_trader_done_blockpost_box&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
                        &amp;lt;actor_dialog&amp;gt;escape_trader_letat_gusi&amp;lt;/actor_dialog&amp;gt;&lt;br /&gt;
	&amp;lt;/specific_character&amp;gt;&lt;br /&gt;
		…&amp;lt;/xml&amp;gt;&lt;br /&gt;
Записываем изменения, и с файлом '''character_desc_escape.xml''' уже все.&lt;br /&gt;
&lt;br /&gt;
'''2)''' Теперь берем файл '''gamedata/config/gameplay/dialogs_escape.xml'''&lt;br /&gt;
&lt;br /&gt;
Диалогу:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Меченый: Сидрыч а почему это у тебя зеленые человечки, что по столу бегают, такие худые? &lt;br /&gt;
Сидорович: Чего?!!&lt;br /&gt;
Меченый: Ты их совсем, совсем не кормишь?&lt;br /&gt;
Сидорович: В следующий раз, как пойдешь в зону, бери-ка  вместо водяры побольше антирада. А то мало что таким перегаром дышишь, уже до зеленых человечков долечился… Шутник.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
который мы хотим реализовать, будет соответствовать такая структура:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&amp;lt;phrase id=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;text&amp;gt;escape_trader_letat_gusi_0&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;escape_trader_letat_gusi_1&amp;lt;/text&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;2&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;text&amp;gt; escape_trader_letat_gusi_2&amp;lt;/text&amp;gt;&lt;br /&gt;
            &amp;lt;next&amp;gt;3&amp;lt;/next&amp;gt;&lt;br /&gt;
&amp;lt;/phrase&amp;gt;&lt;br /&gt;
&amp;lt;phrase id=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;text&amp;gt; escape_trader_letat_gusi_3&amp;lt;/text&amp;gt;&lt;br /&gt;
&amp;lt;/phrase&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Условия наличия ветки в диалоге можно взять из ветки  ''&amp;lt;dialog id=&amp;quot;escape_trader_talk_info&amp;quot;&amp;gt;''&lt;br /&gt;
Т. е. берем условия  &lt;br /&gt;
&amp;lt;code&amp;gt;&amp;lt;precondition&amp;gt;escape_dialog.trader_has_talk_info_wr&amp;lt;/precondition&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
и  &lt;br /&gt;
&amp;lt;code&amp;gt;&amp;lt;has_info&amp;gt;tutorial_end&amp;lt;/has_info&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
(Можно было конечно прописать в скрипте еще одно условие для ветки, чтоб она появилась только один раз, &lt;br /&gt;
а потом больше не возникала. Но об этом как-нибудь позже допишу.)&lt;br /&gt;
&lt;br /&gt;
В итоге у нас получилась такая структура:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;&amp;lt;dialog id=&amp;quot;escape_trader_letat_gusi&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;precondition&amp;gt;escape_dialog.trader_has_talk_info_wr&amp;lt;/precondition&amp;gt;&lt;br /&gt;
        &amp;lt;has_info&amp;gt;tutorial_end&amp;lt;/has_info&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;escape_trader_letat_gusi_0&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;escape_trader_letat_gusi_1&amp;lt;/text&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;2&amp;quot;&amp;gt;&lt;br /&gt;
                                &amp;lt;text&amp;gt; escape_trader_letat_gusi_2&amp;lt;/text&amp;gt;&lt;br /&gt;
                                &amp;lt;next&amp;gt;3&amp;lt;/next&amp;gt;&lt;br /&gt;
                     &amp;lt;/phrase&amp;gt;&lt;br /&gt;
                     &amp;lt;phrase id=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
                                  &amp;lt;text&amp;gt; escape_trader_letat_gusi_3&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;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ее нужно вставить в любом месте между dialog id_шниками других веток в файле '''dialogs_escape.xml'''.&lt;br /&gt;
&lt;br /&gt;
Главное не промахнутся и засунуть именно между, а не внутрь одного из dialog id&lt;br /&gt;
&lt;br /&gt;
Все что находится между тегами &amp;lt;dialog id=&amp;quot;***&amp;quot;&amp;gt; и   &amp;lt;/dialog&amp;gt; это внутенности конкретного dialog id&lt;br /&gt;
&lt;br /&gt;
Т.е совать наш&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;dialog id=&amp;quot;escape_trader_letat_gusi&amp;quot;&amp;gt;&lt;br /&gt;
 …  &lt;br /&gt;
&amp;lt;/dialog&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
надо &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;dialog id=&amp;quot;*** &amp;quot;&amp;gt;&lt;br /&gt;
…&lt;br /&gt;
&amp;lt;/dialog&amp;gt;&lt;br /&gt;
здесь&lt;br /&gt;
&amp;lt;dialog id=&amp;quot;*** &amp;quot;&amp;gt;&lt;br /&gt;
…&lt;br /&gt;
&amp;lt;/dialog&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
но&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;dialog id=&amp;quot;*** &amp;quot;&amp;gt;&lt;br /&gt;
не здесь&lt;br /&gt;
&amp;lt;/dialog&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После сохранения внесенных изменений с файлом '''dialogs_escape.xml''' все.&lt;br /&gt;
&lt;br /&gt;
'''3)''' Теперь вбиваем сами текстовички в файле '''gamedata/config/text/rus/stable_dialogs_escape.xml'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Т.е нам надо в файле '''stable_dialogs_escape.xml''' вставить такую конструкцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;xml&amp;gt;	&amp;lt;string id=&amp;quot;escape_trader_letat_gusi_0&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;text&amp;gt;Сидрыч а почему это у тебя зеленые человечки, что по столу бегают, такие худые?&amp;lt;/text&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&lt;br /&gt;
	&amp;lt;string id=&amp;quot;escape_trader_letat_gusi_1&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;text&amp;gt;Чего?!!&amp;lt;/text&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&lt;br /&gt;
	&amp;lt;string id=&amp;quot;escape_trader_letat_gusi_2&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;text&amp;gt;Ты их совсем, совсем не кормишь?&amp;lt;/text&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&lt;br /&gt;
	&amp;lt;string id=&amp;quot;escape_trader_letat_gusi_3&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;text&amp;gt;В следующий раз, как пойдешь в зону, бери-ка  вместо водяры побольше антирада. А то мало что таким перегаром дышишь, уже до зеленых человечков долечился… Шутник.&amp;lt;/text&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&amp;lt;/xml&amp;gt;&lt;br /&gt;
&lt;br /&gt;
в любом месте между уже существующими string id &lt;br /&gt;
	&amp;lt;pre&amp;gt;&lt;br /&gt;
      &amp;lt;string id=&amp;quot;*** &amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&lt;br /&gt;
              сюда&lt;br /&gt;
	&amp;lt;string id=&amp;quot;*** &amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;/string&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но не внутрь одного из существующих string id&lt;br /&gt;
	&amp;lt;pre&amp;gt;&lt;br /&gt;
      &amp;lt;string id=&amp;quot;*** &amp;quot;&amp;gt;&lt;br /&gt;
               не сюда&lt;br /&gt;
	&amp;lt;/string&amp;gt;&lt;br /&gt;
&amp;lt;/pre&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;pre&amp;gt;&amp;lt;precondition&amp;gt;escape_dialog.trader_has_talk_info_wr&amp;lt;/precondition&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Использование этого условия приведет к вылету &lt;br /&gt;
&lt;br /&gt;
так как со вторым патчем из файла '''escape_dialog.script''' была удалена функция: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lua&amp;gt;function trader_has_talk_info_wr( trader, actor ) &lt;br /&gt;
   return true &lt;br /&gt;
end&amp;lt;/lua&amp;gt;&lt;br /&gt;
&lt;br /&gt;
можно либо воткнуть эту функцию обратно в '''escape_dialog.scrip'''t &lt;br /&gt;
&lt;br /&gt;
либо использовать другие более мение подходящие условия, например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;precondition&amp;gt;escape_dialog.trader_alredy_give_job&amp;lt;/precondition&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Дополение от XiaNi====&lt;br /&gt;
По поводу &amp;lt;precondition&amp;gt; в данном примере.. он тут реально вообще не нужен тем более что возвращает всегда одно и то же... тут уж или реально надо использовать функцию которая хоть чтото проверяет нужное для диалога... а так... диалог и так будет активным если убрать &amp;lt;precondition&amp;gt; (в данном случае это 100%)&lt;br /&gt;
&lt;br /&gt;
''&amp;lt;has_info&amp;gt;'' и ''&amp;lt;dont_has_info&amp;gt;'' это условия на присутствие и соответственно отсутствие у игрока &amp;quot;информации&amp;quot; которая, на самом деле, всего лишь флажок, что пользователь слышал о чем-то или видел что-то который выдается:&lt;br /&gt;
1. условиями &amp;quot;логики&amp;quot; прописанной объектам или неписям.&lt;br /&gt;
2. скриптами&lt;br /&gt;
3. диалогами через например &amp;quot;&amp;lt;give_info&amp;gt;tutorial_end&amp;lt;/give_info&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
В принципе ''&amp;lt;has_info&amp;gt;'' соответствует скриптовому аналогу функции ''has_alife_info''&lt;br /&gt;
так например ''&amp;lt;has_info&amp;gt;tutorial_end&amp;lt;/has_info&amp;gt;'' можно заменить на ''&amp;lt;precondition&amp;gt;escape_dialog.check_tutor_end&amp;lt;/precondition&amp;gt;'' и дописать в '''escape_dialog.script''' строчки :&lt;br /&gt;
&amp;lt;lua&amp;gt;&lt;br /&gt;
function check_tutor_end(actor, npc)&lt;br /&gt;
if	has_alife_info(&amp;quot;tutorial_end&amp;quot;)&lt;br /&gt;
	then&lt;br /&gt;
	 return true&lt;br /&gt;
        else&lt;br /&gt;
         return false&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/lua&amp;gt;&lt;br /&gt;
в которой мы проверяем есть ли информация ''tutorial_end'' у игрока.&lt;br /&gt;
&lt;br /&gt;
==Авторы==&lt;br /&gt;
&lt;br /&gt;
Статья создана:&lt;br /&gt;
&lt;br /&gt;
* [[Участник:BAC9-FLCL|BAC9-FLCL]]&lt;br /&gt;
* [[Участник:Keha|Keha]]&lt;br /&gt;
* [[Участник:XiaNi|XiaNi]]&lt;br /&gt;
[[Категория:Конфигурационные_файлы]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	<entry>
		<id>http://stalkerin.gameru.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B,_%D0%BD%D0%B5_%D0%BF%D1%80%D0%B8%D0%B2%D0%BE%D0%B4%D1%8F%D1%89%D0%B8%D0%B5_%D0%BA_%D0%B2%D1%8B%D0%BB%D0%B5%D1%82%D0%B0%D0%BC_%D0%B8_%D0%B1%D0%BE%D1%8E_%D1%81%D0%B5%D0%B9%D0%B2%D0%BE%D0%B2_(%D1%87%D0%B0%D1%81%D1%82%D1%8C_2)</id>
		<title>Как писать скрипты, не приводящие к вылетам и бою сейвов (часть 2)</title>
		<link rel="alternate" type="text/html" href="http://stalkerin.gameru.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B,_%D0%BD%D0%B5_%D0%BF%D1%80%D0%B8%D0%B2%D0%BE%D0%B4%D1%8F%D1%89%D0%B8%D0%B5_%D0%BA_%D0%B2%D1%8B%D0%BB%D0%B5%D1%82%D0%B0%D0%BC_%D0%B8_%D0%B1%D0%BE%D1%8E_%D1%81%D0%B5%D0%B9%D0%B2%D0%BE%D0%B2_(%D1%87%D0%B0%D1%81%D1%82%D1%8C_2)"/>
				<updated>2013-06-13T11:49:00Z</updated>
		
		<summary type="html">&lt;p&gt;91.217.2.224: /* Необходимые для стабилизации игры правки в модулях */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;
В скриптовом движке есть удобный способ реакции на события - использование коллбэков - процедурных вызовов, привязанных к определённым событиям в жизни игрового объекта - получению повреждений, спавну, смерти и т.д. и т.п. Это hit_callback, death_callback из xr_motivator и многие другие... Все моддеры очень широко и совершенно спокойно пользуются ими, совершенно забывая при этом о таком важном факте, что это - обработки реального времени, как и таймерные события. Что это значит? А собственно вот что... Возьмём для примера коллбэк смерти неписей, мою головную боль последнего времени:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;xr_motivator.script -&lt;br /&gt;
function motivator_binder:death_callback(victim, who)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это функция обрабатывает смерть неписей. Когда вызывается этот коллбек он начинает по очереди вызывать внутренние функции, расположенные в других модулях. Они разрегистрируют умершего непися в гулагах, спавнят в него лут, обновляют статистику, отключают его от доп. схем логики и т.д.&lt;br /&gt;
&lt;br /&gt;
Так вот, обычно, когда в функциях возникает конфликт параметров, бесконечный цикл, попытка индексации nil и т.д., функция вылетает со стандартным логом. '''Но только не в случае, когда она находится внутри коллбэка. Если функция внутри него, то в случае возникновении в ней любой нештатной ситуации, коллбэк наглухо виснет.''' Это происходит из-за того, что функции вызываются строго друг за другом, и каждая из них вызывается только тогда, когда её предшественница вернула управление коллбэку. В случае с death_callback опознать такого непися очень просто - в его трупе окажется фонарик, КПК и возможно ещё немного разных &amp;quot;мусорных&amp;quot; вещей, что говорит о том, что обработка его смерти повисла не дойдя даже до спавна лута. В подобной ситуации можно быть на 100% уверенным, что труп этот не был корректно разрегистрирован, и игра всё ещё считает его живым неписем. Кроме того, зависший коллбэк не освобождает стек (а он у Луа-подсистемы единый на все скрипты), что '''в итоге приводит к вылетам игры с переполнением памяти (вот она, реальная причина этих &amp;quot;родных&amp;quot; вылетов).''' Но было бы слишком хорошо, если бы всё ограничивалось этим... однако тут всё намного хуже... такие &amp;quot;зависшие&amp;quot; коллбэки, особенно если их произошло несколько подряд, очень серьёзно влияют на работу а-лайфа. В лучшем случае они, забивая, стек, мешают нормально работать схемам логики, в худшем вызывают зависания самого  а-лайфа (этот эффект, кстати, производят и сами трупы таких неписей, так как они, как мы помним, не разрегистрировались корректно). '''Основной итог таких событий - бой сейвов, сделанных после возникновения таких ситуаций.''' Если повис один коллбэк, то такие сейвы ещё через раз загружаются, если же несколько, и остановилась работа а-лайфа - всё, сейвы бьются наглухо и реанимации не подлежат.&lt;br /&gt;
&lt;br /&gt;
Поэтому, чтобы избежать такого развития событий, каждый раз, когда вы вносите в коллбэк новую функцию - проверьте её самым тщательным образом. Она не должна содержать никаких рекурсивных циклов, в ней обязательно должны быть проверки на валидность обрабатываемых объектов и значений, и обязательно должна быть обработка нештатных ситуаций - т.е. функция должна обязательно, в абсолютно любой ситуации вернуть управление коллбэку, так или иначе. В самом наихудшем случае - делайте как делали разработчики игры - вставляйте принудительный вылет на рабстол функцией abort - она позволяет передавать отладочное сообщение, и это всяко лучше чем незаметный бой сейвов. Если обработка оборвалась в самом начале, а ф-ция обязательно должна вернуть значение - заведите ей &amp;quot;безопасное&amp;quot; возвращаемое значение по-умолчанию, которое она будет выдавать, если всё пошло плохо. И никогда не пренебрегайте пошаговой отладкой коллбэков с выводом в лог, особенно когда пишете схемы логики - это критически важно для стабильности вашего мода.&lt;br /&gt;
&lt;br /&gt;
== Основные внутриигровые признаки зависания коллбэков типа hit_callback, death_callback ==&lt;br /&gt;
&lt;br /&gt;
1. В трупах попадаются фонарики, КПК, разный мусор и общий лут слишком богат.&lt;br /&gt;
&lt;br /&gt;
2. Частые вылеты во время интенсивных боёв с логами типа&lt;br /&gt;
    Sheduler tried to update object...&lt;br /&gt;
    smart_terrain:1145(1146)&lt;br /&gt;
    LUA: out of memory&lt;br /&gt;
    любой_модуль_логики:любая_cтрока - stack overflow&lt;br /&gt;
&lt;br /&gt;
3. Частые &amp;quot;родные&amp;quot; вылеты в момент смерти непися или попадания по нему&lt;br /&gt;
&lt;br /&gt;
4. Произвольно бьются сейвы во время сражений, выброса и других насыщенных действиями событий&lt;br /&gt;
&lt;br /&gt;
= Использование защищённого кода в LUA =&lt;br /&gt;
&lt;br /&gt;
Периодически случаются такие ситуации, когда мы можем получить вылет при проверке аргумента, и не можем его адекватно заизолировать с помощью предварительной проверки на валидность значения. Вот простой пример: когда я отлаживал '''death_callback''' неписей, я периодически сталкивался с тем, что обращение к методу '''smart_terrain_id()''' при смерти непися иногда вызывало вылет &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;smart_terrain:1143 &amp;quot;attempt to index a nil value&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
- хотя это свойство является родным методом объекта, и отсутствовать напрочь никак не может. В итоге я пришёл к выводу, что его просто иногда не успевает отработать движок, так как вылет этот проявлялся в основном в интенсивных боях и совершенно произвольно.&lt;br /&gt;
&lt;br /&gt;
Вот код, в котором происходил вылет:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function on_death( obj_id )&lt;br /&gt;
--	printf( &amp;quot;on_death obj_id=%d&amp;quot;, obj_id )&lt;br /&gt;
&lt;br /&gt;
	local sim = alife()&lt;br /&gt;
&lt;br /&gt;
	if sim then&lt;br /&gt;
		local obj     = sim:object( obj_id )&lt;br /&gt;
		local strn_id = obj:smart_terrain_id()  --- вылет происходит тут&lt;br /&gt;
&lt;br /&gt;
		if strn_id ~= 65535 then&lt;br /&gt;
			sim:object( strn_id ).gulag:clear_dead(obj_id)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это кусок родного кода из версии 1,0005 игры. Я долго пытался разными способами отсечь этот вылет, вводя предварительные проверки, однако это совершенно ничего не давало - вылет всё равно периодически случался, так как проверки эти сами его вызывали. Тогда я зарылся в документацию по Lua и обнаружил замечательную родную базовую функцию, введённую ещё с первых версий Lua, которой почему-то не пользовались ни разработчики игры, ни моддеры (хотя сама она в Lua сталкера присутствует, и работает отлично, без каких-либо нареканий). Вот она:&lt;br /&gt;
&lt;br /&gt;
'''pcall (f, arg1, ···)'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Вызывает функцию f с указанными через запятую аргументами в защищённом режиме. Это означает, что любая ошибка, даже критическая, внутри вызванной функции, не передаётся наружу - вызывавшей подсистеме. Вместо этого '''pcall''' перехватывает ошибку и возвращает код статуса. Первая возвращаемая переменная это сам код, (true или false) и если всё прошло хорошо, он равен true. В этом случае '''pcall''' сразу после статуса возвращает все результаты от работы защищённой им функции. Если же в защищённой функции произошла ошибка, то '''pcall''' вернёт false и затем сообщение об ошибке. (Обратите внимание, обработка ошибки присходит БЕЗ вылета! Вместо вылета вы получите вполне адекватную строку с ошибкой, которую можно вывести в лог для последующей обработки)&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Я настоятельно советую пользоваться этой функцией в случаях, когда из коллбэков вызываются сложные комплексные обработки, вроде обработки из менеджера вооружений AI-пака. Это позволяет предотвратить как вылеты, так и зависания обработок, и в итоге позволяет хорошо стабилизировать игру.&lt;br /&gt;
&lt;br /&gt;
Возвращаясь к нашим смарттеррейнам... вот как в итоге я подавил вылет типа smart_terrain:1143 с помощью pcall:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;--- эта функция пытается проверить св-во smart_terrain_id объекта. Именно её мы вызовем в защищённом режиме.&lt;br /&gt;
function prot_smt_td(obj)&lt;br /&gt;
	if IsStalker(obj) or IsMonster(obj) then&lt;br /&gt;
		return obj:smart_terrain_id()&lt;br /&gt;
	else	&lt;br /&gt;
		return 65535&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function on_death( obj_id )&lt;br /&gt;
--	printf( &amp;quot;on_death obj_id=%d&amp;quot;, obj_id )&lt;br /&gt;
	local sim = alife()&lt;br /&gt;
	if sim then&lt;br /&gt;
		local obj = sim:object( obj_id )&lt;br /&gt;
		if obj then&lt;br /&gt;
			local strn_id = 65535  --- предварительно проинитим переменную, на &lt;br /&gt;
						--- случай если у нас prot_smt_td выдаст ошибку&lt;br /&gt;
			local result, smt_id = pcall(prot_smt_td,obj)	--- вызываем prot_smt_td в защищённом режиме &lt;br /&gt;
									--- и сразу присваиваем его вывод переменным&lt;br /&gt;
			if result then --- если pcall выдало true&lt;br /&gt;
				strn_id = smt_id  --- тогда применяем полученное значение&lt;br /&gt;
			end&lt;br /&gt;
			--- если же обработка выдаст ошибку, то strn_id останется неизменным...&lt;br /&gt;
			if strn_id ~= 65535 then&lt;br /&gt;
				sim:object(strn_id).gulag:clear_dead(obj_id)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В других местах это делается совершенно аналогично. Подробнее об этой и многих других функция для контроля кода, незаслуженно игнорируемых большинством моддеров, можно почитать тут: http://lua-users.org/wiki/FinalizedExceptions - на английском правда, но захотите - разберётесь, там всё просто. &lt;br /&gt;
&lt;br /&gt;
Настоятельно советую вам изучить работу кода в таких условиях и научиться его правильно применять - этим вы сильно облегчите жизнь как себе, так и тем, кто после вас будет рыться в вашем коде или импортировать его в свои разработки.&lt;br /&gt;
&lt;br /&gt;
= Скрытые критические проблемы в обработке вылетов игрой =&lt;br /&gt;
&lt;br /&gt;
Ведя на днях отладку, выяснил в чём проблема с периодическим боем сейвов и многими другими заморочками как в оригинале игры, так и во многих модах... дело, как выяснилось, далеко не всегда в кривых руках. Есть такая стандартная ф-ция '''abort''' - предназначенная для выкидывания из игры, если что-то пошло не так. И как оказалось, она срабатывает далеко не всегда. Выяснилось это следующим образом:&lt;br /&gt;
&lt;br /&gt;
В одном из логов нашего бета-тестера я увидел '''стандартное сообщение о вылете внутри рабочего лога'''... да-да, то самое которое '''FATAL ERROR''' и дальше по тексту. При этом игра у него НЕ вылетала, это сообщение об ошибке мы обнаружили позже, по случайности. Я заподозрил, что что-то не в порядке, и вставил внутрь этой ф-ции контрольную метку, кидавшую в консоль сообщение, в котором содержался паттерн сообщения об ошибке и само сообщение. Так вот, оказалось, что эта самая функция '''abort''' вызывается в игре с завидным постоянством (вы удивитесь насколько часто), когда возникают исключения в схемах логики, звука и т.д., но игра от этого вылетает на рабочий стол '''максимум только 3 раза из 10 вызовов'''. Вылет НЕ происходит обычно, когда функции передан паттерн ошибки, а остальные параметры пустые, такое бывает, и частенько. И если не сделать внутри этой функции особой метки для вывода в лог, как сделал это я, её вызовы проходят совершенно незаметно, и '''игра после критических ошибок продолжается как ни в чём ни бывало.''' А приводит это вот к чему... Внутри '''xr_logic''' в процедуре записи пстора (хранилища логики и флагов) неписей есть вызовы этого самого аборта в случае если на запись в пстор передана некорректная величина. Ну а так как аборт периодически вообще не срабатывает, то часто попадается ситуация, что неписям в пстор пишется полный ахтунг: куски кода из ОЗУ, всякая муть из лтх-ов, куски аллспавна, всё что угодно. Происходит это оттого, что кодер, писавший эту функцию ('''xr_logic.pstor_store(obj, varname, val)'''), явно и думать не думал что '''abort''' может не сработать. У него запись в пстор стояла после проверки, а не внутри неё (very bad idea), и если abort не срабатывал, игра писала в пстор мусор совершенно спокойно и незаметно для игрока. Потом вся эта хрень попадала прямо в сейвы. Вот проблемный код для наглядности:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function pstor_store(obj, varname, val)&lt;br /&gt;
	local npc_id = obj:id()&lt;br /&gt;
	if db.storage[npc_id].pstor == nil then&lt;br /&gt;
		db.storage[npc_id].pstor = {}&lt;br /&gt;
	end&lt;br /&gt;
	local tv = type(val)&lt;br /&gt;
	if not pstor_is_registered_type(tv) then&lt;br /&gt;
		abort(&amp;quot;xr_logic: pstor_store: not registered type '%s' encountered&amp;quot;, tv) --- вот тут мы должны если что вылететь&lt;br /&gt;
	end&lt;br /&gt;
	db.storage[npc_id].pstor[varname] = val -- а если не вылетели, всё, получим запись в пстор левой мути&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Разумеется игра этим пстором в итоге давится, и сейвы сделанные после такой милой записи практически лопаются. Результат - &amp;quot;битые&amp;quot; (на самом деле подлежат реанимации) сейвы. Происходит это потому, что игра из сейва грузит неписям псторы сплошным чтением по словам, пока они не закончатся. В случае же если в псторе обнаруживается записанный ранее мусор, то обработка либо вылетает сразу, либо наглухо виснет, пытаясь запихать эдак с миллион слов в пстор особо отличившегося непися. Как вам например непись с размером пстора в 1697451 слова? В результате попытки его обработать игра просто на стадии синхронизации выжрала всю доступную ОЗУ и повисла. &lt;br /&gt;
&lt;br /&gt;
Решение этой проблемы оказалось достатоно простым: во-первых я предположил максимальный размер полезной части пстора неписей в 20 слов r_u32() (пока ориентировочно, я ещё уточняю эту величину), и соответственно сделал остановку цикла загрузки пстора для неписей через 20 итераций. Там же, где в цикле стояла проверка на валидность записываемых данных (кстати тоже с вылетом в случае провала проверки), я сделал так, что если параметр не относится к валидному типу данных, то запись параметра в пстор не производится совсем. Это необходимо для того, чтобы если вдруг в сейве обнаружится мусор, то он был бы просто отброшен обработкой. Практика показала, что в итоге такие неписи вполне адекватны и в дальнейшем никаких проблем не вызывают, так как начало их пстора, с нормальными данными, обычно не повреждается - мусор дописывается после них, а не вместо них. &lt;br /&gt;
&lt;br /&gt;
Ну и во-вторых модифицировал запись параметров в пстор, просто убрав запись под основание if-else так, чтобы если параметр неверен, он не записывался совсем. Теперь кстати, очень интересно, сохранились ли те же заморочки с ф-цией '''abort''' в Чистом Небе, и если да, то останутся ли в Зове Припяти?&lt;br /&gt;
&lt;br /&gt;
Примечание другого автора: в Зове Припяти большая часть вылетов всё также не вылетает. Вы можете себя неприятно удивить, если раскомментируете в файле _g.script строку&lt;br /&gt;
&amp;lt;code lua&amp;gt;--	error_log(reason)&amp;lt;/code&amp;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_logic.script''&lt;br /&gt;
&lt;br /&gt;
'''Было:'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function pstor_store(obj, varname, val)&lt;br /&gt;
	local npc_id = obj:id()&lt;br /&gt;
	if db.storage[npc_id].pstor == nil then&lt;br /&gt;
		db.storage[npc_id].pstor = {}&lt;br /&gt;
	end&lt;br /&gt;
	local tv = type(val)&lt;br /&gt;
	if not pstor_is_registered_type(tv) then&lt;br /&gt;
		abort(&amp;quot;xr_logic: pstor_store: not registered type '%s' encountered&amp;quot;, tv)&lt;br /&gt;
	end&lt;br /&gt;
	db.storage[npc_id].pstor[varname] = val&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Стало:'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function pstor_store(obj, varname, val)&lt;br /&gt;
	if not obj then return end&lt;br /&gt;
	local npc_id = obj:id()&lt;br /&gt;
	if db.storage[npc_id].pstor == nil then&lt;br /&gt;
		db.storage[npc_id].pstor = {}&lt;br /&gt;
	end&lt;br /&gt;
	local tv = type(val)&lt;br /&gt;
	if not pstor_is_registered_type(tv) then&lt;br /&gt;
		dgblog(&amp;quot;xr_logic: pstor_store: not registered type encountered - write in pstor_store cancelled&amp;quot;)&lt;br /&gt;
		-- abort убран, так как один хрен не работает. Пусть тогда хотя бы в лог что-то валится.&lt;br /&gt;
	else&lt;br /&gt;
		db.storage[npc_id].pstor[varname] = val&lt;br /&gt;
		-- вот так и только так. Если значение не валидно, ничего не происходит.&lt;br /&gt;
	end	&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''А эта правка выкинет из пстора весь мусор при загрузке сейва, если он как-то в него попал'''&lt;br /&gt;
&lt;br /&gt;
'''Было:'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function pstor_load_all(obj, reader)&lt;br /&gt;
	local npc_id = obj:id()&lt;br /&gt;
	local pstor = db.storage[npc_id].pstor&lt;br /&gt;
	if not pstor then&lt;br /&gt;
		pstor = {}&lt;br /&gt;
		db.storage[npc_id].pstor = pstor&lt;br /&gt;
	end&lt;br /&gt;
	local ctr = reader:r_u32()&lt;br /&gt;
	for i = 1, ctr do&lt;br /&gt;
		local varname = reader:r_stringZ()&lt;br /&gt;
		local tn = reader:r_u8()&lt;br /&gt;
		if tn == pstor_number then&lt;br /&gt;
			pstor[varname] = reader:r_float()&lt;br /&gt;
		elseif tn == pstor_string then&lt;br /&gt;
			pstor[varname] = reader:r_stringZ()&lt;br /&gt;
		elseif tn == pstor_boolean then&lt;br /&gt;
			pstor[varname] = reader:r_bool()&lt;br /&gt;
		else&lt;br /&gt;
			abort(&amp;quot;xr_logic: pstor_load_all: not registered type N %d encountered&amp;quot;, tn)&lt;br /&gt;
		end&lt;br /&gt;
		printf(&amp;quot;_bp: pstor_load_all: loaded [%s]='%s'&amp;quot;, varname, utils.to_str(pstor[varname]))&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Стало:'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function pstor_load_all(obj, reader)&lt;br /&gt;
	local npc_id = obj:id()&lt;br /&gt;
	local pstor = db.storage[npc_id].pstor&lt;br /&gt;
	if not pstor then&lt;br /&gt;
		pstor = {}&lt;br /&gt;
		db.storage[npc_id].pstor = pstor&lt;br /&gt;
	end&lt;br /&gt;
	local ctr = reader:r_u32()&lt;br /&gt;
	if tonumber(ctr) &amp;gt; 20 and tostring(obj:name()) ~= &amp;quot;single_player&amp;quot; and npc_id ~= db.actor:id() then&lt;br /&gt;
		-- максимум 20 итераций - это число ещё уточняется, возможно понадобится больше&lt;br /&gt;
                -- если у вас в пстор что-то свое пишется, ориентируйтесь на свои значения&lt;br /&gt;
		-- и обязательно убираем из проверки актора - у него очень толстый пстор, и к тому же&lt;br /&gt;
                -- если уж поврежденным будет его пстор, то тут точно уже ничего не поможет&lt;br /&gt;
		dgblog(&amp;quot;ОБНАРУЖЕН ОБЪЕКТ С ПОВРЕЖДЕННЫМ PSTOR: &amp;quot;..tostring(obj:name())..&lt;br /&gt;
&amp;quot; БУДЕТ ПРОИЗВЕДЕНА ПОПЫТКА ВОССТАНОВЛЕНИЯ&amp;quot;)&lt;br /&gt;
		ctr = 20 &lt;br /&gt;
	end&lt;br /&gt;
	for i = 1, ctr do&lt;br /&gt;
		local varname = reader:r_stringZ()&lt;br /&gt;
		local tn = reader:r_u8()&lt;br /&gt;
		if tn == pstor_number then&lt;br /&gt;
			pstor[varname] = reader:r_float()&lt;br /&gt;
		elseif tn == pstor_string then&lt;br /&gt;
			pstor[varname] = reader:r_stringZ()&lt;br /&gt;
		elseif tn == pstor_boolean then&lt;br /&gt;
			pstor[varname] = reader:r_bool()&lt;br /&gt;
		else&lt;br /&gt;
			-- не надо пытаться вылетать - просто не пишем поврежденные данные&lt;br /&gt;
			-- при этом обязательно удалять саму переменную - в результате записи&lt;br /&gt;
 			-- мусора в пстор одно только ее название может повесить загрузку&lt;br /&gt;
			pstor[varname] = nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти примеры приведены для чистой игры. Единственное в чем не уверен пока, это в том, что максимум полезного размера - 20 слов. Возможно нужно выделить больше, это надо будет проверить ещё экспериментальным путем...&lt;br /&gt;
&lt;br /&gt;
Кроме этого, советую ещё внутрь ф-ции abort вставить отладочные метки, чтобы точно знать когда она вызывалась. Настоятельно советую сделать это даже если вы матёрый моддер со стажем - гарантирую, будете неприятно удивлены.&lt;br /&gt;
&lt;br /&gt;
Я это сделал вот так:&lt;br /&gt;
&lt;br /&gt;
''_g.script''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;-- Крешнуть игру (после вывода сообщения об ошибке в лог)&lt;br /&gt;
function abort(fmt, msg)&lt;br /&gt;
	local message = tostring(msg)&lt;br /&gt;
	dbglog(&amp;quot;ERROR PATTERN: &amp;quot;..tostring(fmt))&lt;br /&gt;
	dbglog(&amp;quot;ERROR REASON: &amp;quot;..message)&lt;br /&gt;
	local reason = string.format(fmt, message)&lt;br /&gt;
	assert(&amp;quot;ERROR: &amp;quot; .. reason)&lt;br /&gt;
	printf(&amp;quot;ERROR: &amp;quot; .. reason)&lt;br /&gt;
	dbglog(&amp;quot;%s&amp;quot;, reason)&lt;br /&gt;
	printf(&amp;quot;%s&amp;quot;)&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
По поводу частичной неработоспособности ф-ции '''abort''' я беседовал с Колмогором, и он пришёл к выводу, что видимо вылет игры должен был бы производиться при обработке функции '''printf(&amp;quot;%s&amp;quot;)''' - ей тут передаётся заведомо отсутствующий оператор и она по уму должна бы сразу крашить игру. Однако в релизе функция printf фактически не работает(она реализована в _g.script через вырезанную функцию log). В результате игра не крашится. Но что поделать, в итоге мне пришлось модифицировать его таким образом, чтобы вылет при его срабатывании был гарантирован:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;-- Крешнуть игру (после вывода сообщения об ошибке в лог)&lt;br /&gt;
function abort(fmt, msg)&lt;br /&gt;
	local message = tostring(msg)&lt;br /&gt;
	dbglog(&amp;quot;ERROR PATTERN: &amp;quot;..tostring(fmt))&lt;br /&gt;
	dbglog(&amp;quot;ERROR REASON: &amp;quot;..message)&lt;br /&gt;
	local reason = string.format(fmt, message)&lt;br /&gt;
	assert(&amp;quot;ERROR: &amp;quot; .. reason)&lt;br /&gt;
	printf(&amp;quot;ERROR: &amp;quot; .. reason)&lt;br /&gt;
	dbglog(&amp;quot;%s&amp;quot;, reason)&lt;br /&gt;
	printf(&amp;quot;%s&amp;quot;)&lt;br /&gt;
	local crash&lt;br /&gt;
	local ooops = 1/crash&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вылет происходит при попытке произвести арифметическую операцию с неинициализированной переменной crash.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Дополнение от cjayho (ecb team):&amp;lt;/b&amp;gt; Вообще крэшить игру ошибкой в скриптовом коде - далеко не самое очевидное решение. В windows-подобных системах статус, возвращаемый программой после ее выполнения, не имеет ни малейшего смысла, поэтому корректно ли мы выключим игру или некорректно - разницы не будет никакой. Поэтому есть смысл сделать крэш игры более очевидным и стопроцентно работающим способом: использовать консольную команду &amp;lt;b&amp;gt;quit&amp;lt;/b&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;-- Крешнуть игру (после вывода сообщения об ошибке в лог)&lt;br /&gt;
function abort(fmt, msg)&lt;br /&gt;
	local message = tostring(msg)&lt;br /&gt;
	dbglog(&amp;quot;ERROR PATTERN: &amp;quot;..tostring(fmt))&lt;br /&gt;
	dbglog(&amp;quot;ERROR REASON: &amp;quot;..message)&lt;br /&gt;
	local reason = string.format(fmt, message)&lt;br /&gt;
	assert(&amp;quot;ERROR: &amp;quot; .. reason)&lt;br /&gt;
	printf(&amp;quot;ERROR: &amp;quot; .. reason)&lt;br /&gt;
	dbglog(&amp;quot;%s&amp;quot;, reason)&lt;br /&gt;
	get_console():execute( 'quit' )&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
А для тех кто задается вопросом: &amp;lt;i&amp;gt;почему разработчики не сделали так изначально?&amp;lt;/i&amp;gt; отвечу: вероятнее всего ошибка в скрипте была не очень очевидным заклинанием по призыву волшебного зеленого жука, которое после выпуска не-дебаг версии потеряло свой изначальный сакральный смысл.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Лечение зависаний алайфа при смерти персонажей ==&lt;br /&gt;
&lt;br /&gt;
Недавно, отлаживая проблемы с зависанием алайфа, мне удалось найти причину этого периодически во всех модах всплывающего сбоя, приводящего к порче сейвов и сильно мешающего нормально играть. Сбой этот возникает при смерти некоторых NPC, обычно квестовых. В частности в моём случае изолировать и отладить это зависание удалось на Юрике, новичке со Свалке, учавствующем в сцене с гоп-стопом. Причина оказалась в обработке посмертной отрегистрации NPC из гулагов, причём сбой там был настоящей матрёшкой, составной из нескольких частей. Правок в итоге было совсем немного, но чтобы сделать их мне пришлось несколько часов распутывать клубок из кросс-вызовов между скриптами smart_terrain и xr_gulag. Итак, начнём с самого начала. Работая над OGSE 069 и 0691 я периодически сталкивался с зависаниями и вылетами в посмертных обработках неписей. Один из таких вылетов - всем хорошо знакомый вылет:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;smart_terrain:1143 &amp;quot;attempt to index a nil value&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Происходящий в функции '''smart_terrain.on_death( obj_id )''' - я тогда его заблокировал вызовом его внутри безопасного кода функцией '''pcall''', однако, как теперь выяснилось, этого оказалось недостаточно - баг тут состоит из нескольких частей, и этот вылет указывает только на одну из них. Вот исходный код:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function on_death( obj_id )&lt;br /&gt;
--	printf( &amp;quot;on_death obj_id=%d&amp;quot;, obj_id )&lt;br /&gt;
&lt;br /&gt;
	local sim = alife()&lt;br /&gt;
&lt;br /&gt;
	if sim then&lt;br /&gt;
		local obj     = sim:object( obj_id )&lt;br /&gt;
		local strn_id = obj:smart_terrain_id()  --- первый вылет/зависнаие алайфа происходит тут&lt;br /&gt;
&lt;br /&gt;
		if strn_id ~= 65535 then&lt;br /&gt;
			sim:object( strn_id ).gulag:clear_dead(obj_id) -- а вот в этой обработке происходит зависание алайфа. Она очень комплексная, и её сложно распутывать.&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Во-первых выяснилось, что изредка вызов '''obj:smart_terrain_id()''' вызывает зависание алайфа даже когда он производится изнутри защищённого кода. Тогда я решил избавиться от использования этой функции в данном месте совсем. После нескольких экспериментов выяснилось, что самым простым, быстрым и вылетобезопасным способом будет считать нетпакет существа в таблицу и выудить идентификатор смарттеррейна из неё. Для этого можно написать свою обработку, однако я, как весьма ленивый программист, не склонен изобретать велосипеды, поэтому я воспользовался уже проверенной у нас и активно используемой в OGSE библиотекой функций для работы с нетпакетами '''m_net_utils''' Артоса. Кроме того, я сразу сделал более безопасным вызов обработки на отрегистрацию в гулагах. Вот, собственно, что в итоге получилось:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;function on_death( obj_id )&lt;br /&gt;
--	printf( &amp;quot;on_death obj_id=%d&amp;quot;, obj_id )&lt;br /&gt;
	local sim = alife()&lt;br /&gt;
	if sim then&lt;br /&gt;
		local obj = sim:object( obj_id )&lt;br /&gt;
		if (obj and obj.smart_terrain_id) then&lt;br /&gt;
			local strn_id = 65535  -- значение по умолчанию&lt;br /&gt;
			&lt;br /&gt;
			local t = nil -- сюда запихнём табличку из пакета&lt;br /&gt;
			if IsStalker(obj) then t = m_net_utils.get_stalker_data(obj) elseif IsMonster(obj) then t = m_net_utils.get_monster_data(obj) end &lt;br /&gt;
			-- вызываем парсинг пакета для неписей и монстров отдельно&lt;br /&gt;
			&lt;br /&gt;
			-- print_table_inlog(t)&lt;br /&gt;
			if t.smtrid then&lt;br /&gt;
				strn_id = tonumber(t.smtrid) -- получаем идентификатор смарта, если его нету даже в пакете, хрен с ним, будет 65535&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			if strn_id ~= 65535 then -- если сняли идентификатор, попробуем отрегать...&lt;br /&gt;
				local gulag = sim:object(strn_id)&lt;br /&gt;
				if gulag and gulag.gulag then -- ...но сначала выясним если вообще такой гулаг и инициализирован ли он&lt;br /&gt;
					sim:object(strn_id).gulag:clear_dead(obj_id)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом сделаю отсупление и предупрежу об одном очень странном сбое с которым я столкнулся редактируя эту функцию. Так вот, всё нормально работает только тогда когда у ф-ции этой есть строго определённая структура. '''Стоит только добавить пару строк, убрать закомментированную и сдвинуть пару условий, просто в тексте сдвинуть, не меняя внутренней логики, как игра начинает вылетать, причём ещё до загрузки сейва, при кэшировании (!) и с очень странными логами, хотя код написан синтаксически безупречно! Например с таким:'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Description: xr_gulag:1035 value not found ObjectJobPathName[obj_id]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Вернёшь строки на место, перестроишь текст - работает снова. Совершенная чертовщина, шаманил я над этой функцией около получаса, перестраивая текст таким образом чтобы игра нормально запускалась.''' Имейте это в виду когда в неё полезете и если что не пугайтесь если игра начнёт так вылетать - в этом случае пошаманьте немного над ней, меняя её форматирование. О причинах такого поведения я не могу даже догадываться - то ли движок её вызывает по смещению внутри файла вручную, то ли это какой-то баг Lua-парсера, но факт фактом.&lt;br /&gt;
&lt;br /&gt;
Итак, теперь проблема с получением идентификатора смарта разрешилась, однако алайф всё равно зависал! Простая трассировка показала, что теперь зависание происходило внутри обработки&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;sim:object(strn_id).gulag:clear_dead(obj_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
И мне пришлость распутывать её по частям, доискиваясь до причины. Обработка честно говоря мерзкая, размазана по smart_terrain и xr_gulag, при этом взаимные вызовы идут не менее десятка раз, в следующем стиле: ф-ция в смарте вызывает ф-цию в гулаге, которая вызывает фцию в смарте, которая обрабатывает параметр фцией в гулаге, который передётся ф-цией в логике, которая получает её из смарта. Нечто подобное. Опуская все нецензурные выражения, употребленные мной при трассировке, я лучше расскажу что собственно вышло в итоге. А в итоге я вышел вот на этот код в xr_gulag, именно в нём при отрегистрации некоторых мёртвых неписей происходит зависание алайфа:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;-- освободить объект от работы и переинициализировать логику.&lt;br /&gt;
-- если сталкер в онлайне и начал работу, то сбросить его схему поведения&lt;br /&gt;
-- как будто он только что загрузился&lt;br /&gt;
function gulag:free_obj_and_reinit( obj_id )&lt;br /&gt;
	self:free_obj(obj_id)&lt;br /&gt;
&lt;br /&gt;
	local t = self.Object[obj_id]&lt;br /&gt;
	if t ~= nil and t ~= true and self.Object_begin_job[obj_id] then&lt;br /&gt;
		xr_logic.initialize_obj( t, nil, false, db.actor, self:get_stype( obj_id ) ) -- вот эта обработка вешает алайф&lt;br /&gt;
	end&lt;br /&gt;
end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Я, честно говоря, ни разу не понимаю, на кой чёрт нужно переконфигурировать схемы логики и выбирать из них активную '''трупу, который лежит себе спокойно и никого не трогает.''' Может быть в это есть некий высший смысл, или это было продиктовано неким аккуратизмом, однако одно я могу сказать однозначно - подобная переинициализация у некоторых трупов неписей приводит к глухому зависанию алайфа. При этом если для трупов эту обработку заблокировать, то трупы разрегистрируются вполне нормально и спокойно лежат с нетронутой логикой, не вызывая никаких проблем. Итоговый код после правок выглядит вот так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;-- освободить объект от работы и переинициализировать логику.&lt;br /&gt;
-- если сталкер в онлайне и начал работу, то сбросить его схему поведения&lt;br /&gt;
-- как будто он только что загрузился&lt;br /&gt;
function gulag:free_obj_and_reinit( obj_id )&lt;br /&gt;
	self:free_obj(obj_id)&lt;br /&gt;
	local t = self.Object[obj_id]&lt;br /&gt;
	if t ~= nil and t ~= true and self.Object_begin_job[obj_id] then&lt;br /&gt;
		if check_game() then -- тут проверяется, запущена ли игра, если ли актор и жив ли он. Если да, делаем по новому.&lt;br /&gt;
			local s_obj = alife():object(obj_id) -- проверим есть ли у цели разрегистрации валидный серверный объект&lt;br /&gt;
			if s_obj and (IsStalker(s_obj) or IsMonster(s_obj)) and s_obj:alive() then -- если есть, он жив и сталкер или монстр&lt;br /&gt;
				xr_logic.initialize_obj( t, nil, false, db.actor, self:get_stype( obj_id ) ) -- только тогда инициализируем логику&lt;br /&gt;
			end&lt;br /&gt;
		else -- а если игра не запущена, то как раньше. Это нужно для того, чтобы обработка запуска игры нормально работала.&lt;br /&gt;
			xr_logic.initialize_obj( t, nil, false, db.actor, self:get_stype( obj_id ) )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Проверка, запущена ли игра&lt;br /&gt;
function check_game()&lt;br /&gt;
	if level.present() and (db.actor ~= nil) and db.actor:alive() then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После этих поправок зависания алайфа при смерти неписей удалось побороть окончательно. Решение с отрезанием реинита логики для трупов несколько грубовато, но честно говоря, у меня нет ни малейшего желания трассировать и разбирать на части функцию xr_logic.initialize_obj, выясняя, чем же ей так данный конкретный труп не приглянулся. Если хотите - займитесь, найдёте причину - дополните данную статью. Я же успокоился на том, что заблокировал баг, периодически убивающий людям игру, так как многие пользователи, игнорируя предупреждения, играют на одном-двух сейвах, а то и вовсе на квиксейвах всю игру.&lt;br /&gt;
&lt;br /&gt;
= Другие частые проблемы =&lt;br /&gt;
&lt;br /&gt;
Спустя некоторое время я обнаружил причины ещё нескольких часто встречающихся вылетов, и решил записать их описание сюда - эта информация наверняка ещё много кому пригодится.&lt;br /&gt;
&lt;br /&gt;
== Вылеты при удалении объектов из игры ==&lt;br /&gt;
&lt;br /&gt;
При использовании для удаления объектов родной движковой функции '''alife():release(alife():object(id), true)''' возможен целый ворох разнообразнейших вылетов, обычно - безлоговых, что сильно затрудняет их отладку. Вот из-за чего они возникают:&lt;br /&gt;
&lt;br /&gt;
1) Вылет при удалении непися или монстра, находящегося в онлайне.&lt;br /&gt;
   Решение: с помощью alife():release '''можно удалять только мёртвые объекты'''. Поэтому если вам нужно удалить с её помощью непися или монстра, который жив и находится в онлайне, его нужно убить любым доступным методом, хотя бы нанеся ему hit() с любым запредельным уроном по вкусу.&lt;br /&gt;
&lt;br /&gt;
2) Вылет при удалении оружия или артефакта.&lt;br /&gt;
   Решение: такая проблема часто встречается в случае если объект неудачно расположен или находится в руках у непися. Для того чтобы не произошло вылета, убедитесь что объект доступен как серверный перед удалением. Вот так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;	local obj = alife():object(i)&lt;br /&gt;
	if obj then&lt;br /&gt;
		alife():release(obj, true)&lt;br /&gt;
	end&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эту конструкцию вообще желательно использовать всегда, когда вы так удаляете объекты.&lt;br /&gt;
&lt;br /&gt;
3) Вылет при удалении аномалии.&lt;br /&gt;
   Решение: аномалии - очень капризные при подобном с ними обращении объекты. Они влияют на своё окружение, и если рядом с ними находится непись или монстр, то удаление такой аномалии приведёт к вылету игры. Чтобы этого не произошло, аномалию надо сначала выключить функцией '''disable_anomaly''', и удалять затем ТОЛЬКО тогда, когда она не будет занята влиянием на динамический объект. Для этого нужно получить список мобов на локации, и из их нетпакетов считать идентификаторы действующих на них рестрикторов. Если ваша аномалия будет в этом списке - удалять её нельзя. Дождитесь пока она освободится.&lt;br /&gt;
&lt;br /&gt;
== Вылет при открытии закладки &amp;quot;Контакты&amp;quot; в ПДА ==&lt;br /&gt;
&lt;br /&gt;
Простой безлоговый вылет при открытии закладки &amp;quot;Контакты&amp;quot;. Встречался во всех крупных модах, и никто не знал как его излечить. А лечится он банально - '''его причина - дублирование идентификаторов секций в XML-файле, описывающем иконки неписей для закладки &amp;quot;Контакты&amp;quot;'''. Нужно всего лишь проверить этот файл на наличие дублированных идентификаторов и удалить их. Вылет пропадёт и никогда больше не будет встречаться.&lt;br /&gt;
&lt;br /&gt;
== Вылеты при вызове несуществующих функций из XML ==&lt;br /&gt;
&lt;br /&gt;
В ХML-файлах, используемых для описания инфопоршенов, для многих инфопоршенов прописаны действия, которые игра вызывает при взятии этого инфопоршена. Вот так примерно:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code xml&amp;gt;&lt;br /&gt;
	&amp;lt;info_portion id=&amp;quot;barman_document_have&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;action&amp;gt;dialogs.set_actor_prebandit1&amp;lt;/action&amp;gt;&lt;br /&gt;
		&amp;lt;action&amp;gt;bar_spawn.bandits2&amp;lt;/action&amp;gt;&lt;br /&gt;
		&amp;lt;action&amp;gt;bar_spawn.bandits3&amp;lt;/action&amp;gt;&lt;br /&gt;
		&amp;lt;action&amp;gt;bar_spawn.bandit7&amp;lt;/action&amp;gt;&lt;br /&gt;
		&amp;lt;action&amp;gt;bar_spawn.bandit8&amp;lt;/action&amp;gt;&lt;br /&gt;
	&amp;lt;/info_portion&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так вот, если вы допустите опечатку в названии вызываемой функции или же случайно её удалите - игра будет стабильно вылетать без лога при взятии этого инфопоршена.&lt;br /&gt;
&lt;br /&gt;
== Повреждения сейвов на Радаре и других местах в модах, основанных на OGSM ==&lt;br /&gt;
&lt;br /&gt;
В оригинале ОГСМ и основанных на нём модах часто встречались проблемы с сохранениями на Радаре. Эту проблему долго не удавалось победить, пока наконец благодаря помощи Маландринуса не удалось выявить её первопричину. Как выяснилось, она очень проста - гражданские зомби в моде (монстры) имели в конфиге ту же пропись вида (параметр конфига specie), что и монолитовцы и зомбированные (неписи). И там и тут было проставлено &amp;quot;zombie&amp;quot;, и так оно было ещё с оригинала. Как оказалось, так делать категорически нельзя. Дело в том, что у неписей есть такой функционал, как хитовая память - в ней какое-то время хранятся ссылки на атакующие объекты. У монстров тоже есть остатки этого функционала, но он неработоспособен, и использовать его нельзя. В случае же когда монстры и неписи попадают в один вид, в ситуации когда они находятся рядом в бою, хитовая память монстров автоматически получает от неписей того же вида распространяемую внутри вида информацию об атакующих - а хранить её монстрам нельзя. Если после создания такой ситуации сохраниться - сейв будет вызывать вылет при загрузке. То есть проще говоря, если в бою с монолитовцами рядом оказывались гражданские зомби - и игрок сохранял игру - сейв этот не загружался. Чтобы предотвратить эти проблемы, вполне достаточно создать для гражданских зомби свой отдельный вид, добавив его прописи в конфиг game_relations.&lt;br /&gt;
&lt;br /&gt;
== Застывания NPC после боя / лечения ранения в модах, использующих дополнительные схемы поведения ==&lt;br /&gt;
&lt;br /&gt;
Во многих модах, использующих дополнительные схемы поведения часто можно заметить NPC, которые после боя застывают, прицелившись в одну точку. Аналогично часто это встречается с вылеченными от ранения NPC - они встают и замирают намертво, до тех пор пока их не выведет из этого состояния атаковавший враг. Как правило сейв/загрузка этой проблемы не решают. В ходе работы над OGSE 0.6.9.3 мне удалось выяснить причину таких проблем и успешно её устранить. Причина проблемы заключается в том, что NPC управляются не только скриптовыми схемами, но и движком, и для переключения управления между одним и другим используется скрипт ''state_mgr'' - менеджер состояний. Его директивы имеют наивысший приоритет. Аналогичный же приоритет себе как правило назначают доп. схемы поведения, используя пропись эвалуатора в ''xr_motivatior.addCommonPrecondition(action)''. В итоге при выходе из движковой боевки или при переключении на движковый алайф (это происходит после излечения ранения) доп. схемы поведения вступают с менеджером состояний в конфликт, блокируя смену состояния NPC. Для того чтобы этого не происходило, '''необходимо во всех схемах поведения, использующих принудительное назначение состояния через функцию ''state_mgr.set_state'' сделать блокировку перехвата управления менеджером состояний''', добавив соответствующие прописи в биндер. Вот таким образом, как в этом примере - тут я правил биндер схемы лечения/самолечения ''xrs_medic'':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code lua&amp;gt;&lt;br /&gt;
function add_to_binder(object, ini, scheme, section, storage)&lt;br /&gt;
	local operators	= {}&lt;br /&gt;
	local properties  = {}&lt;br /&gt;
&lt;br /&gt;
	local manager = object:motivation_action_manager()&lt;br /&gt;
&lt;br /&gt;
	operators[&amp;quot;medic&amp;quot;]			= actid_medic&lt;br /&gt;
	operators[&amp;quot;self_medic&amp;quot;]		= actid_self_medic&lt;br /&gt;
&lt;br /&gt;
	properties[&amp;quot;medic&amp;quot;]			= evid_medic&lt;br /&gt;
	properties[&amp;quot;self_medic&amp;quot;]	= evid_self_medic&lt;br /&gt;
	&lt;br /&gt;
	local state_mgr_to_idle_combat = xr_actions_id.state_mgr + 1 ---&amp;lt; Это переключение на движковую боевку, но оно тут не использовано&lt;br /&gt;
	local state_mgr_to_idle_alife = xr_actions_id.state_mgr + 2 ---&amp;lt; Это переключение на движковый алайф&lt;br /&gt;
	local state_mgr_to_idle_off = xr_actions_id.state_mgr + 3   ---&amp;lt; Это переключение в статичное состояние&lt;br /&gt;
&lt;br /&gt;
	local zombi=object:character_community()==&amp;quot;zombied&amp;quot; or object:character_community()==&amp;quot;trader&amp;quot; or&lt;br /&gt;
		  object:character_community()==&amp;quot;arena_enemy&amp;quot; or object:name()==&amp;quot;mil_stalker0012&amp;quot; or object:name()==&amp;quot;yantar_ecolog_general&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if zombi then&lt;br /&gt;
		manager:add_evaluator (properties[&amp;quot;medic&amp;quot;], property_evaluator_const(false))&lt;br /&gt;
		manager:add_evaluator (properties[&amp;quot;self_medic&amp;quot;], property_evaluator_const(false))&lt;br /&gt;
	else&lt;br /&gt;
		manager:add_evaluator (properties[&amp;quot;medic&amp;quot;], evaluator_medic(&amp;quot;medic&amp;quot;, storage))&lt;br /&gt;
		manager:add_evaluator (properties[&amp;quot;self_medic&amp;quot;], evaluator_self_medic(&amp;quot;self_medic&amp;quot;, storage))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local action = action_medic (object,&amp;quot;medic&amp;quot;, storage)&lt;br /&gt;
	action:add_precondition(world_property(stalker_ids.property_alive, true))&lt;br /&gt;
	action:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base, false))&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;medic&amp;quot;], true))&lt;br /&gt;
	action:add_effect (world_property(properties[&amp;quot;medic&amp;quot;], false))&lt;br /&gt;
	manager:add_action (operators[&amp;quot;medic&amp;quot;], action)&lt;br /&gt;
	&lt;br /&gt;
	local action = action_self_medic (object,&amp;quot;self_medic&amp;quot;, storage)&lt;br /&gt;
	action:add_precondition(world_property(stalker_ids.property_alive, true))&lt;br /&gt;
	action:add_precondition(world_property(stalker_ids.property_enemy,false))	&lt;br /&gt;
	action:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base, false))&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;medic&amp;quot;], false))&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;self_medic&amp;quot;], true))&lt;br /&gt;
	action:add_effect (world_property(properties[&amp;quot;self_medic&amp;quot;], false))&lt;br /&gt;
	manager:add_action (operators[&amp;quot;self_medic&amp;quot;], action)&lt;br /&gt;
		&lt;br /&gt;
	action = manager:action (stalker_ids.action_alife_planner)	&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;medic&amp;quot;], false))&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;self_medic&amp;quot;], false))&lt;br /&gt;
	&lt;br /&gt;
	action = manager:action(state_mgr_to_idle_alife)&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;self_medic&amp;quot;], false)) ---&amp;lt; Блокируем попытки переключиться на движковый алайф пока работает самолечение&lt;br /&gt;
&lt;br /&gt;
	action = manager:action(state_mgr_to_idle_off)&lt;br /&gt;
	action:add_precondition	(world_property(properties[&amp;quot;self_medic&amp;quot;], false)) ---&amp;lt; Блокируем попытки переключиться статичное состояние пока работает самолечение&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вставка этих директив в биндеры всех схем, которые меняют состояния принудительно устраняет конфликты, и NPC больше не виснут при смене схем.&lt;br /&gt;
&lt;br /&gt;
== Зависания алайфа из-за ошибок в конфигах торговли ==&lt;br /&gt;
&lt;br /&gt;
Очень частая проблема у начинающих модостроителей. Обнаруживается боем сейвов в радиусе алайфа от торговца с некорректным конфигом. Обычно главная и единственная причина этой проблемы - наличие в секции ''buy_supplies'' секций, у которых не прописан один или оба параметра, или же наличие записей вида &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
novice_outfit				;NO TRADE&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Запомните - в секции ''buy_supplies'' конфига торговли таких записей быть не должно вообще. Если вы хотите убрать вещь из общего ассортимента - удалите её строку из ''buy_supplies''.&lt;br /&gt;
&lt;br /&gt;
--[[Участник:Kamikazze|KamikaZze (OGSE Team)]] 11:04, 31 марта 2011 (UTC)&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;
Статья создана: [[Участник:Kamikazze|Kamikazze]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Скрипты]]&lt;/div&gt;</summary>
		<author><name>91.217.2.224</name></author>	</entry>

	</feed>