Уроки скриптологии

Уроки скриптологии, часть 1.


Тема первая: Типы переменных.

В скриптах используется всего несколько типов переменных:
void - пустой тип
int - целое число
float - число с плавающей точкой
string - строка текста
class - структура с внутренними типами и функциями
prototype - обычно инициализация класса, но возможно и другое использование
instance - пока сказать нечего.
вот, кажется, и все типы.


Тема вторая: Операции над данными.

Существует несколько типов операций: арифметические, присваивания, отношения, логические, побитовые, над массивами, операции с членами классов.
В основном, все операции применимы только к простым типам, за исключением типа string.

1. Арифметические операции.

+ - простое сложение
- - простое вычитание
* - простое умножение
/ - простое деление (результат целая часть)
% - простое деление (результат остаток)

2. Операции присваивания.

= - простое присваивание
+= - сложение с приваиванием
-= - вычитание с присваиванием
*= - умножение с присваиванием
/= - деление с приваиванием (целая часть)

3. Операции отношения.

< - истина, если меньше
> - истина, если больше
<=- истина, если меньше или равно
>= - истина, если больше или равно
== - истина, если равно
!= - истина, если не равно

4. Логические операции.

! - истина, если выражение ложно
|| - истина, если истинно значение первого или второго операнда
&& - истина, если истинно значение первого и второго операнда
Операция ! является только префиксной операцией (пример !операнд)

5. Побитовые операции.

~ - инверсия операнда (префиксная операция)
>> - логический сдвиг вправо операнда1 на величину операнда2
<< - логический сдвиг влево операнда1 на величину операнда2
| - побитовая операция ИЛИ
& - побитовая операция И
^ - побитовая операция исключающее ИЛИ

6. Операции над массивами.

[] - получение или задание элемента массива по индексу (при инициализации массива задание его размерности)

7. Операции с членами классов.

. - обращение к элементу класса
Пример: аа.бб , где аа - имя класса, бб - имя переменной класса

8. Другие операции.

() - вызов функции с аргументами


Тема третья: Операторы и идентификаторы типа.

1. Операторы.

Операторов в скриптах реализовано очень мало:
{ выражение (я) };- оператор блок, после закрывающей фигурной скобки обязательна точка с запятой.
Блоки допускают вложения друг в друга.
if (условие(я)) блок - оператор условного выполнения. Если условие истина, то выполняется блок, исли условие ложно, то блок пропускается. Возможна и другая реализация:
if (условие(я)) блок else блок - если условие истина, выполняется только блок if, если условие ложь, то выполняется только блок else. Возможно вложение операторов if друг в друга.
return - оператор выхода из функции, может возвращать значение.

2. Идентификаторы типа.

Существуют два идентификатора типа:
const- определяет тип константы
var - определяет тип переменной
Оба идентификатора типа применимы к следующим типам: int, float, string, func.
Идентификатор var может использоваться только внутри блока.


Тема четвертая: Грамматика скриптов.

Текст скриптов состоит из файлов с расширением .D, файлы желательно располагать по определенным темам, согласно директорий, можно создавать любое кол-во файлов и директорий с произвольными именами.
Процесс компиляции начинается с файла с расширением .SRC, в нем записывается порядок, в котором будут компилироваться файлы .D. Допускается вложенность файлов .SRC.

Одно общее правило: прежде чем использовать какую-либо переменную, она должна быть декларирована. Декларация переменной с определенным именем во всех скриптах должна быть одна. Декларация (объявление) может быть с инициализацией или без нее.
Формат .D файла прост: любая декларация должна начинаться с одного из ключевых слов: const, var, func, class, instance, prototype. Идентификатор var, как говорилось ранее, может быть использован только в блоке.
Компилятор одинаково распознает как заглавные буквы, так и строчные буквы, поэтому имена Аааа и аааа - это одно и то же.
Комментарии могут быть любые и в любом месте файла, они начинаются с // и до конца строки, или выделяются конструкцией /* комментарий */ и могуть включать несколько строк.

Существует несколько главных файлов .SRC, их имена изменять нельзя. Это в папке Content - gothic.src и fight.src, а в папке System - camera.src, menu.src, music.src, sfx.src, visualfx.src и particlefx.src -из них компилируются соответствующие *.dat файлы.
Дальше будем рассматривать базовые классы и функции. Все конкретные классы и функции будут относиться только к скриптам gothic.src. Остальные скрипты пока рассматривать не будем, может потом в этом появится необходимость.
Небольшое замечание: В скриптах кол-во локальных переменных в блоке и размерность любого массива ограничены числом 255.


Тема пятая: Классы.

Все классы описаны в файле ..\_intern\Classes.d

1. Класс параметров НПС.

const int MAX_CHAPTER    = 5;   //Макс. количество глав в игре (не используется)
const int MAX_MISSIONS   = 5;   //Макс. количество одновременно выполняемых миссий
const int MAX_HITCHANCE  = 5;   //Макс. размерность массива способностей владения видами оружия

class c_npc
{
   var int     id;                         //Однозначный идентификатор, связывающий конкретного НПС с его параметрами
   var string  name[5];                    //Имя НПС, оно появляется на экране, если НПС находится в фокусе
   var string  slot;                       //(не используется) Можно использовать для хранения какой-нибудь строки текста!
   var string  effect;                     //Имя визуального эффекта, привязанного к НПС
   var int     npcType;                    //Тип НПС (описывается в файле ..AI\AI_Intern\AI_Constants.d)
   var int     flags;                      //Флаги НПС (NPC_FLAG_IMMORTAL, NPC_FLAG_GHOST)
   var int     attribute[ATR_INDEX_MAX];   //Массив значений атрибутов (ATR_HITPOINTS, ATR_MANA, ATR_STRENGTH и т.д.)
   var int     HitChance[MAX_HITCHANCE];   //Способность владения НПС определенным оружием
   var int     protection[PROT_INDEX_MAX]; //Массив значений защиты от определенных повреждений
   var int     damage[DAM_INDEX_MAX];      //Массив значений наносимых повреждений
   var int     damagetype;                 //Тип повреждения (НПС наносит в данный момент)
   var int     guild;                      //Принадлежность к гильдии
   var int     level;                      //Уровень НПС
   var func    mission[MAX_MISSIONS];      //(не используется)
   var int     fight_tactic;               //Тактика сражения (описывается в файле ..AI\AI_Intern\AI_Constants.d)
   var int     weapon;                     //(не используется)
   var int     voice;                      //Номер голоса в диалогах
   var int     voicePitch;                 //Тональность голоса
   var int     bodymass;                   //(не используется)
   var func    daily_routine;              //Функция распорядка дня
   var func    start_aistate;              //Функция начального AI состояния (ZS_ функции)
   var string  spawnPoint;                 //Имя Waypoint, в котором рождается НПС
   var int     spawnDelay;                 //Задержка в секундах между смертью и возрождением НПС
   var int     senses;                     //Чувства (зрение, слух, обоняние)
   var int     senses_range;               //Диапазон чувств в см.
   var int     aivar[100];                 //Массив значений AI переменных (описывается в файле ..AI\AI_Intern\AI_Constants.d), неиспользуемые индексы могут
 использоваться для собственных целей.
   var string  wp;                         //Текущий Waypoint, в котором находится НПС (отслеживается Gothic.exe)
   var int     exp;                        //Количество полученной экспы (только для ГГ)
   var int     exp_next;                   //Количество экспы оставшееся до следующего уровня (только для ГГ)
   var int     lp;                         //Свободные очки обучения (только для ГГ)
   var int     bodyStateInterruptableOverride; //Флаг возможности прерывания действия НПС (если TRUE - НПС прервать нельзя)
   var int     noFocus;                    //Флаг, отвечающий за фокус (если TRUE и НПС находится в фокусе, то у НПС не показывается имя и количество жизни)
};

2. Класс миссий (не используется).

class c_mission
{
   var string  name;
   var string  description;
   var int     duration;
   var int     important;
   var func    offerConditions;
   var func    offer;
   var func    successConditions;
   var func    success;
   var func    failureConditions;
   var func    failure;
   var func    obsoleteConditions;
   var func    obsolete;
   var func    running;
};

3. Класс параметров предмета.

class c_item
{
   var int     id;                         //Однозначный идентификатор, связывающий конкретный предмет с его параметрами
   var string  name;                       //Имя предмета, оно появляется на экране, если предмет находится в фокусе
   var string  nameID;                     //(не используется) Можно использовать для хранения какой-нибудь строки текста!
   var int     hp,hp_max;                  //???
   var int     mainflag;                   //Флаги категории предмета
   var int     flags;                      //Остальные флаги предметов
   var int     weight,value;               //Вес и стоимость предмета
   var int     damageType;                 //Тип наносимого повреждения (используется для оружия)
   var int     damageTotal;                //Суммарное повреждение (используется для оружия)
   var int     damage[DAM_INDEX_MAX];      //Величина ущерба по типам повреждений (используется для оружия)
   var int     wear;                       //Флаги ношения предмета
   var int     protection[PROT_INDEX_MAX]; //Величина защиты от конкретных повреждений (используется для брони)
   var int     nutrition;                  //(не используется)
   var int     cond_atr[3];                //Массив атрибутов, требуемых для применения
   var int     cond_value[3];              //Массив значений атрибутов, требуемых для применения
   var int     change_atr[3];              //(не используется)
   var int     change_value[3];            //(не используется)
   var func    magic;                      //(не используется)
   var func    on_equip;                   //Функция вызываемая при экипировке
   var func    on_unequip;                 //Функция вызывается, когда предмет снимается или убирается в инвентарь
   var func    on_state[4];                //Функции вызываемые при использовании предмета по видам состояний (видов состояний не обнаружил ???)
   var func    owner;                      //Владелец предмета, записывается ID НПС (не понятен тип переменной FUNC ???)
   var int     ownerGuild;                 //Какой гильдии принадлежит предмет
   var int     disguiseGuild;              //(не используется)
   var string  visual;                     //Имя .3DS файла текстур предмета
   var string  visual_change;              //Имя .ASC файла визуальных эффектов при получении этого предмета
   var string  effect;                     //Имя визуального эффекта, привязанного к предмету
   var int     visual_skin;                //Вариация текстуры для .3DS файла текстур предмета
   var string  scemeName;                  //Внутреннее имя предмета
   var int     material;                   //Материал предмета
   var int     munition;                   //Тип заряда для оружия дальнего радиуса поражения
   var int     spell;                      //Номер заклинания, которое вызывает данный предмет
   var int     range;                      //Радиус поражения оружием ближнего боя
   var int     mag_circle;                 //Круг магии, к которому принадлежит предмет
   var string  description;                //Название предмета в инвентаре
   var string  text[ITM_TEXT_MAX];         //Массив строк описания предмета в инвентаре
   var int     count[ITM_TEXT_MAX];        //Массив значений характеристик предмета в инвентаре (соответствует определенной текстовой строке)
   // Характеристики изображения предмета в инвентаре
   var int     inv_zbias;                  //Масштаб предмета в инвентаре
   var int     inv_rotx;                   //Поворот предмета в инвентаре относительно оси X
   var int     inv_roty;                   //Поворот предмета в инвентаре относительно оси Y
   var int     inv_rotz;                   //Поворот предмета в инвентаре относительно оси Z
   var int     inv_animate;                //Вращение предмета в инвентаре
};

4. Класс параметров фокуса.

class c_focus
{
   // для НПС
   var float   npc_longrange;              //Ширина
   var float   npc_range1,npc_range2;      //Диапазон дальности
   var float   npc_azi;                    //Азимут
   var float   npc_elevdo,npc_elevup;      //Тангаж
   var int     npc_prio;                   //Приоритет
   // для предметов
   var float   item_range1,item_range2;    //Диапазон дальности
   var float   item_azi;                   //Азимут
   var float   item_elevdo,item_elevup;    //Тангаж
   var int     item_prio;                  //Приоритет
   // для MOB's
   var float   mob_range1,mob_range2;      //Диапазон дальности
   var float   mob_azi;                    //Азимут
   var float   mob_elevdo,mob_elevup;      //Тангаж
   var int     mob_prio;                   //Приоритет
};

5. Класс параметров диалогов.

class c_info
{
   var int     npc;                        //ID НПС, которому принадлежит диалог
   var int     nr;                         //Опция диалога с большим nr будет отображаться в списке ниже
   var int     important;                  //Флаг начала диалога (TRUE - диалог начинаешь ты, FALSE - с тобой)
   var func    condition;                  //Функция проверки условий начала диалога (пока функция не возвратит TRUE, диалог не начнется)
   var func    information;                //Функция управления диалогом
   var string  description;                //Строка, которая выводится в окно выбора вариантов ответов
   var int     trade;                      //Возможность торговли (TRUE - возможно торговать, FALSE - нет)
   var int     permanent;                  //Постоянность диалога (TRUE - диалог повторяется при каждом обращении, FALSE - однократный)
};

6. Класс реакций на предметы (не используется).

class c_itemreact
{
   var int     npc;
   var int     trade_item;
   var int     trade_amount;
   var int     requested_cat;
   var int     requested_item;
   var int     requested_amount;
   var func    reaction;
};

7. Класс параметров заклинаний.

class c_spell
{
   var float   time_per_mana;                //Время в мс, требуемое для инвестирования маны на один уровень
   var int     damage_per_level;             //Наносимый урон за уровень заклинания
   var int     damageType;                   //Тип повреждения
   var int     spellType;                    //Категория заклинания
   var int     canTurnDuringInvest;          //Возможность поворота во время инвестирования маны (TRUE - возможно)
   var int     canChangeTargetDuringInvest;  //Возможность выбора цели во время инвестирования маны (TRUE - возможно)
   var int     isMultiEffect;                //Возможность мультиэффектов заклинания (множественные цели и др.)
   var int     targetCollectAlgo;            //Константа описания цели
   var int     targetCollectType;            //Тип цели заклинания
   var int     targetCollectRange;           //Дальность действия
   var int     targetCollectAzi;             //Угол азимута
   var int     targetCollectElev;            //Угол тангажа
};

8. Глобальные ссылки на классы.

instance self,other(c_npc);  // Сам НПС и Другой НПС
instance victim(c_npc);      // НПС - жертва определенного действия (3ий в действии)
instance item(c_item);       // Сам предмет
instance hero(c_npc);        // Сам ГГ (игрок)


Тема шестая: Функции.

Несколько общих замечаний:
1. Функции могут возвращать значения только этих типов: void, int, float, string, instance.
2. Рассматривать будем пока только базовые функции, которые реализованы в екзешнике.
3. Большинство базовых функций описано в файле externals.d, но в нем есть ошибки (этот файл приведен только для справки, он не компилируется), о них буду говорить по мере разбора функций, и отмечать значком ***.
4. Идентификаторы func (при объявлении функции) и var (как идентификатор аргументов) для простоты приводить не буду.

1. Функции вывода текста.

void Print (string s0); - выводит на экран строку текста s0, как OutputUnits текст (аналогично выводу из OU файла).
void PrintMulti (string s0, string s1, string s2, string s3, string s4); - выводит текст на экран, объединяя строки параметров s0- s 4 в одну строку.
int PrintScreen (string msg, int posx, int posy, string font, int timeSec); - выводит на экран строку текста msg (имя шрифта – font) с координатами posx, posy (диапазон от 0 до 99% размера экрана, -1 означает вывод по центру соответствующей оси экрана) на время timeSec (в секундах). Возвращаемое значение всегда = 0.***
void PrintDebug (string s); - выводит текст строки s через zSpy или в лог. файл, работает только в отладочном режиме (включается (Alt+D))
void PrintDebugInst (string text); - выводит текст строки text через zSpy или в лог. файл для ссылки self в отладочном режиме.
void PrintDebugInstCh (int ch, string text); - выводит текст строки text через zSpy или в лог. Файл для ссылки self канала ch в отладочном режиме (каналы определены в файле PrintDebug. d).
void PrintDebugCh (int ch, string text); - выводит текст строки text через zSpy или в лог. файл для канала ch в отладочном режиме
int PrintDialog (int dialogNr, string msg, int posx, int posy, string font, int timeSec); - работает аналогично функции PrintScreen, только вывод осуществляется в окно диалога c номером dialogNr (координаты в % действуют внутри окна диалога).***

2. Функции преобразования типов.

string IntToString (int x); - преобразовывает целое число x в строку.
string floatToString (float x); - преобразовывает число с плавающей точкой x в строку.***
int floatToInt (float x); - преобразовывает число с плавающей точкой x в целое число.
float IntTofloat (int x); - преобразовывает целое число х в число с плавающей точкой.
string ConcatStrings (string str1, string str2); - результирующей строкой является объединение двух строк str1 и str2.

3. Функции работы со звуком.

void Snd_Play (string s0); - воспроизвести звуковой файл с именем s0.
void Snd_Play3D (c_npc n0, string s1); - воспроизвести звуковой файл с именем s0 для НПС n0.
int Snd_IsSourceNpc (c_npc self); - проверить, является ли НПС self источником звука (return > 0) и установить ссылку other на этого НПС.
int Snd_IsSourceItem (c_npc self); - проверить, является ли у НПС self предмет источником звука (return > 0) и установить ссылку item на этот предмет
int Snd_GetDistToSource (c_npc self); - выдать расстояние в сантиметрах до источника звука от НПС self.

4. Вспомогательные функции.

int Hlp_Random (int n0); - сгенерировать случайное целое число в диапазоне от 0 до n0 - 1.
int Hlp_StrCmp (string s1, string s2); - сравнивает две строки s1 и s2, возвращает TRUE при равенстве строк или FALSE при неравенстве.
int Hlp_IsValidNpc (c_npc self); - если ссылка на НПС self существует, то возвращает TRUE, иначе – FALSE.
int Hlp_IsValidItem (c_item item); - если ссылка на предмет item существует, то возвращает TRUE, иначе – FALSE.
int Hlp_IsItem (c_item item, int instanceName); - проверяет принадлежность предмета item к соответствующему типу instanceName, возвращает TRUE при соответствии, иначе – FALSE.
c_npc Hlp_GetNpc (int instanceName); - возвращает ссылку на НПС, принадлежащему к типу instanceName.
int Hlp_GetInstanceID (c_npc npc); - получить ID номер npc по ссылке на класс npc.
int Hlp_GetInstanceID (c_item item); - получить ID номер предмета по ссылке на класс item.

5. Функции работы с документами (свитки, книги, записки, карты).

int Doc_Create (); - создает новый документ и возвращает номер его обработчика.
int Doc_CreateMap (); - создает новую карту и возвращает номер её обработчика. ***
void Doc_SetLevel (int handle, string world); - устанавливает уровень world (формат ZEN) для документа handle.*** (Не знаю, что конкретно делает эта функция).
void Doc_SetPages (int handle, int pages); - задает кол-во отображаемых страниц pages для документа handle.
void Doc_SetPage (int handle, int page, string pageimage, int scale); - устанавливает параметры страницы page (если параметр page = -1, то применяется ко всем страницам) документа handle: pageimage – имя файла изображения страницы в формате TGA, scale = 0 - применять масштабируемось, scale = 1 - не использовать масштабируемость (применяется для карт).
void Doc_SetFont (int handle, int page, string fontname); - задать шрифт для страницы page документа handle, где fontname – имя файла шрифта в формате TGA.
void Doc_SetMargins (int handle, int page, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int pixel); - установить границы вывода текста на странице page документа handle, где leftMargin, topMargin, rightMargin, bottomMargin – соответственно левая, верхняя, правая и нижняя границы, pixel – кол-во пикселей на единицу границы.
void Doc_PrintLine (int handle, int page, string text); - выводит строку текста text на странице page документа handle.
void Doc_PrintLines (int handle, int page, string text); - выводит текст text в несколько строк на странице page документа handle.
void Doc_Show (int handle); - выводит созданный документ handle на экран.
void Doc_Open (string picName); - открывает документ с именем picName, как рисунок на заднем плане (в скриптах не используется).
void Doc_Font (string fontName); - задает для документа шрифт fontName (в скриптах не используется).
void Doc_Print (string text); - выводит текст text документа на экран (в скриптах не используется).
void Doc_MapCoordinates (string levelName, float gamex1, float gamey1, float pixelx1, float pixely1, float gamex2, float gamey2, float pixelx2, float pixely2); - инициализирует карту с именем уровня levelName, где gamex1, gamey1, gamex2, gamey2 – координаты углов карты, соответствующие координатам уровня, а pixelx1, pixely1, pixelx2, pixely2 – соответственно коодинаты пикселей углов карты (в скриптах не используется).

6. Функции работы с журналом квестов.

void Log_CreateTopic (string name, int section); - создать в журнале тему с именем name в разделе section (предопределены два раздела LOG_MISSION или LOG_NOTE).
void Log_SetTopicStatus (string name, int status); - установить статус status темы с именем name (предопределены следующие статусы - LOG_RUNNING, LOG_SUCCESS, LOG_FAILED, LOG_OBSOLETE).
void Log_AddEntry (string topic, string entry); - записать в тему topic текст из строки entry.

7. Функции работы с миссиями.

void Mis_SetStatus (int missionName, int newStatus); - установить статус newStatus миссии missionName (предопределены следующие статусы RUNNING, SUCCESS, FAILED и т.д.)
int Mis_GetStatus (int missionName); - получить статус миссии missionName.
int Mis_OnTime (int missionName); - проверка миссии, возвращает TRUE, если миссия активна, т.е. не завершена. Примечание: данная функция в экзешнике реализована неверно, она ничего не возвращает (лучше ее не использовать).
void Mis_AddMissionEntry (c_mission miss, string name); - добавить в список миссий новую миссию miss под именем name.***
void Mis_RemoveMission (c_mission miss); - удалить из списка миссий миссию miss. ***

8. Функции работы с инвентарем.

void CreateInvItem (c_npc n0, int n1); - создает в инвентаре НПС n0 предмет с ID n1.
void CreateInvItems (c_npc n0, int n1, int n2); - создает в инвентаре НПС n0 предметы с ID n1 количеством n2.
void EquipItem (c_npc n0, int n1); - экипировать НПС n0 предметом с ID n1.
void Mob_CreateItems (string mobName, int itemInstance, int amount); - создает в контейнере с именем mobName (например сундук) предметы с ID itemInstance количеством amount.
int Mob_HasItems (string mobName, int itemInstance); - получить количество предметов с ID itemInstance, находящихся в контейнере с именем mobName.

9. Функции анимации.

void Mdl_ApplyOverlayMds (c_npc n0, string s1); - запустить файл анимации s1 с расширением mds для НПС n0.
void Mdl_RemoveOverlayMDS (c_npc self, string overlayName); - отключить от НПС self файл анимации overlayName.
void Mdl_ApplyOverlayMDSTimed (c_npc self, string overlayname, int timeTicks); - запустить файл анимации overlayname для НПС self на время timeTicks.***
void Mdl_ApplyRandomAni (c_npc n0, string s1, string s2); - запустить случайные анимации s1 для НПС n0 в последовательности анимационных состояний s2.
void Mdl_ApplyRandomAniFreq (c_npc n0, string s1, float f2); - устанавливает частоту выборки f2 случайных анимационных состояний s1 для НПС n0.
void Mdl_StartFaceAni (c_npc self, string name, float intensity, float holdTime); - запускает файл name анимации лица для НПС self с интенсивностью intensity (в %, 1 – 100%) и временем удержания holdTime (1 – постоянно).
void Mdl_ApplyRandomFaceAni (c_npc self, string name, float timeMin, float timeMinVar, float timeMax, float timeMaxVar, float probMin); - запускает файл случайной анимации лица name для НПС self со следующими параметрами : timeMin, timeMax – диапазон времени воспроизведения анимации в сек, timeMinVar, timeMaxVar – отклонения от значений timeMin, timeMax в сек, probMin – вероятность использования отклонения (от 0 до 1) от нижней до верхней границы.
void Mdl_SetVisual (c_npc self, string fileMds); - задать для НПС self файл изображения с именем fileMds (формат MDS).***
void Mdl_SetVisualBody (c_npc self, string body, int bodytex, int color, string head, int htex, int ttex, int armor); - установить параметры изображения НПС self, где : body – имя mesh файла изображения тела, bodytex – номер текстуры тела, color – номер палитры цвета кожи, head – имя MMS файла изображения головы, htex – номер текстуры головы, ttex – номер текстуры зубов, armor – номер одетой брони (- 1 – брони нет).***
void Mdl_SetModelScale (c_npc self, float x, float y, float z); - установить масштаб mesh изображения модели НПС self по координатам x, y, z в % (1 -100%).
void Mdl_SetModelFatness (c_npc self, float fatness); - установить жирность mesh изображения модели НПС self в % (1 - 100%). Может кто-нибудь пояснить, что это за функция?

10. Функции распорядка дня.

void TA_Min (c_npc self, int start_h, int start_m, int stop_h, int stop_m, func state, string waypoint); - для НПС self задается функция распорядка дня state, где start_h и start_m – время в часах и минутах начала выполнения функции, stop_h и stop_m – соответственно время окончания функции, waypoint – имя точки, в которой выполняется функция.
void TA (c_npc self, int start_h, int stop_h, func state, string waypoint); - выполняет все то, что и предыдущая функция, только без привязки к минутам.
void TA_CS (c_npc self, string csName, string roleName); - функция, выполняемая последней в списке функций распорядка дня для НПС self, где csName – имя файла CS (Cutscene), roleName – роль НПС, которая должна выполняться. (частично не понял)
void TA_BeginOverlay (c_npc self); - начать выполнять заявленный распорядок дня для НПС self.
void TA_EndOverlay (c_npc self); - закончить выполнять заявленный распорядок дня для НПС self.
void TA_RemoveOverlay (c_npc self); - удалить заявленный распорядок дня для НПС self.

11. Функции работы с диалогами.

int InfoManager_HasFinished (); - показывает, завершен ли диалог (возвращает 0 во время диалога и 1 после окончания диалога).***
void Info_AddChoice (c_info menu, string text, func fn); - добавляет в меню диалога menu строку выбора действия text, fn – функция, которая активируется при выборе данного пункта меню.***
void Info_ClearChoices (c_info menu); - удалить тему диалога menu.***

12. Функции различного назначения.

void ExitGame (); - просто закончить игру.***
int PlayVideo (string video); - показать видео файл video (c расширением BIK), возвращает 1, если видео показано, 0 – в случае ошибки.***
void SetPercentDone (int perc); - установить процент perc отображения полоски в окне загрузки (ProgressBar), значение от 0 до 100.***
void IntroduceChapter (string chapter, string name, string file, string sound, int time); - вывести на экран окно новой главы, где chapter – номер главы, name – название главы, file – файл заставки главы (формат TGA), sound – звуковой файл (формат WAV), time – время показа заставки.***
void Tal_Configure (int tal, int value); - переопределить значение таланта value для константы tal (например, определены константы NPC_TALENT_PICKLOCK, NPC_TALENT_MAGE и т.д.).***
void Perc_SetRange (int percID, int range); - устанавливает дальность действия range пассивного восприятия percID в сантиметрах.
void Rtn_Exchange (string oldRoutine, string newRoutine); - заменяет функции oldRoutine распорядка дня НПС self на новые функции newRoutine. (Имя функции должно начинаться с RTN_ и заканчиваться идентификатором НПС скрипта).
int Hlp_CutscenePlayed (string csName); - информирует, игралась ли Cutscene с именем csName (0 – нет, 1 – да).

13. Функции работы с миром Готики.

int Wld_DetectNpc (c_npc self, int instance, func aiState, int guild); - эта функция инициализирует глобальную переменную скриптов other, отличную от НПС self, где instance – производная от класса c_npc, которая должна быть найдена и проинициализирована (-1 – любая производная), guild – гильдия, членом которой должен быть искомый НПС (-1 – любая гильдия), aiState – функция AI состояния, в котором должен находится искомый НПС (NOFUNC – любое AI состояние). Функция возвращает 1 в случае успешного завершения (other инициализирован найденным НПС), 0 – неудача (other не определен).
int Wld_DetectNpcEx (c_npc self, int instance, func aiState, int guild, int detectPlayer); - выполняет все тоже, что и предыдущая функция, дополнительный параметр detectPlayer указывает, исключить ли игрока (ГГ) из поиска (0 – исключить, 1 – нет).
int Wld_DetectItem (c_npc self, int flags); - эта функция инициализирует глобальную переменную скриптов item предметом, возможно находящимся у НПС self и имеющим флаг flags, возвращает 1 при успешном поиске и инициализации, иначе – 0.
int Wld_DetectPlayer (c_npc self); - возвращает 1, если ГГ есть НПС self, иначе – 0.
void Wld_SetGuildAttitude (int guild1, int attitude, int guild2); - установить отношение между гильдиями, где guild1, guild2 – гильдии, между которыми устанавливается отношение, attitude – отношение между гильдиями (определены отношения ATT_HOSTILE, ATT_FRIENDLY, ATT_NEUTRAL, ATT_ANGRY).
int Wld_GetGuildAttitude (int guild1, int guild2); - получить отношение между гильдиями guild1 и guild2.
int Wld_IsMobAvailable (c_npc self, string schemeName); - проверяет, есть ли MOB с именем schemeName в окрестности 10 метров от НПС self, возвращает TRUE, если MOB существует и свободен, иначе FALSE.
int Wld_GetMobState (c_npc self, string schemeName); - определяет состояние MOB с именем schemeName для НПС self, возвращает состояние или -1, если MOB не найден.
int Wld_IsFPAvailable (c_npc self, string fpName); - проверяет, есть ли FP (Freepoint) с именем fpName в окрестности 20 метров от НПС self, возвращает TRUE, если FP существует и свободна, иначе FALSE.
int Wld_IsNextFPAvailable (c_npc self, string fpName); - работает аналогично предыдущей функции, но проверится ближайшая точка, если fpName блокирована.
int Wld_GetDay (); - получить текущий день, отсчет дней идет от дня старта (Gamestart) = 0.
int Wld_IsTime (int hour1, int min1, int hour2, int min2); - возвращает 1, если текущее время находится между границами (hour1, min1 – нижняя граница, hour2, min2 – верхняя граница в часах и минутах), иначе возвращает 0.
void Wld_SetTime (int hour, int min); - установить текущее время в часах hour и минутах min.***
void Wld_InsertNpc (int npcInstance, string spawnPoint); - разместить в мире одного НПС, где npcInstance – ссылка на НПС, spawnPoint – имя точки размещения (может быть как WP, так и FP).
void Wld_InsertNpcAndRespawn (int instance, string spawnPoint, int spawnDelay); - выполняется аналогично предыдущей функции, только НПС после смерти будет оживлен в заданной точке через spawnDelay секунд.***
void Wld_SpawnNpcRange (c_npc self, int npcInstance, int number, float time); - НПС self около себя создает НПС (ссылка на него npcInstance) количеством number на время жизни time. Используется в заклинаниях вызова.***
void Wld_RemoveNpc (int npcInstance); - удалить из мира НПС (ссылке на него npcInstance).***
void Wld_InsertItem (int itemInstance, string spawnPoint); - разместить в мире один предмет, где itemInstance – ссылка на предмет, spawnPoint – имя точки размещения (может быть как WP, так и FP). В примечании к функции написано: «Осторожно, не функционирует! Предмет будет вставлен в центр мира.» В скриптах данная функция используется, осталось выяснить, как она работает.
int Wld_RemoveItem (c_item item); - удалить из мира предмет по item ссылке на него, возвращает 1 – успешное удаление, иначе – 0.
void Wld_InsertObject (string name, string point); - разместить в мире визуальный объект name в точке с именем point (может быть как WP, так и FP).***
void Wld_SetObjectRoutine (int hour1, int min1, string objName, int state); -объект с именем objName (VOB объект) перейдет в состояние state в момент времени hour1, min1 (часы и минуты).
void Wld_SetMobRoutine (int hour1, int min1, string objName, int state); - выполняется как и предыдущая функция, только для MOB объекта.
void Wld_SendTrigger (string name); - активировать функцию VOB объекта с именем name. (Я не знаю где где прописаны эти функции).***
void Wld_SendUntrigger (string name); - деактивировать функцию VOB объекта с именем name.***
void Wld_ExchangeGuildAttitudes (string name); - установить взаимоотношения между гильдиями, где name – имя таблицы заданных отношений.
void Wld_AssignRoomToGuild (string s0, int guild); - размещает гильдию guild на пространстве s0.
void Wld_AssignRoomToNpc (string s0, c_npc roomowner); - размещает НПС roomowner на пространстве s0.
c_npc Wld_GetPlayerPortalOwner ();
int Wld_GetPlayerPortalGuild ();
c_npc Wld_GetFormerPlayerPortalOwner ();
int Wld_GetFormerPlayerPortalGuild ();
Какие-то четыре функции, определяющие положение игрока в пространстве, большего сказать, увы, не могу.
void Wld_PlayEffect (string ???, instance ???, instance ???, int ???, int ???, int ???, int ???); - данная функция абсолютно нигде не применяется и не описана, поэтому ее действие и аргументы мне не известны.***

14. Функции работы с НПС.

int Npc_GetTRUEGuild (c_npc npc); - возвращает номер гильдии, к которой принадлежит НПС npc.
int Npc_SetTRUEGuild (c_npc npc, int guildID); - установить принадлежность НПС npc к гильдии guildID.
void Npc_SetAttitude (c_npc self, int att); - установить постояноое отношение att НПС self ко всем остальным НПС.
void Npc_SetTempAttitude (c_npc self, int att); - установить временное отношение att НПС self ко всем остальным НПС.
int Npc_GetAttitude (c_npc self, c_npc other); - получить любое отношение НПС self к НПС other.
int Npc_GetPermAttitude (c_npc self, c_npc other); - получить постоянное отношение НПС self к НПС other.
int Npc_GetGuildAttitude (c_npc self, c_npc other); - получить отношение НПС self к НПС other, как отношение между гильдиями, а не личностями.
void Npc_SetKnowsPlayer (c_npc self, c_npc player) ; - эта функция задает, что НПС self знаком с НПС player (ГГ).
int Npc_KnowsPlayer (c_npc self, c_npc player); - проверить, знаком ли НПС self с НПС player, возвращает TRUE, если знаком, иначе FALSE.
int Npc_OwnedByNpc (c_item item, c_npc npc); - проверить, принадлежит ли предмет item npc, возвращает 1, если принадлежит, иначе - 0.
int Npc_OwnedByGuild (c_item item, int guild); - - проверить, принадлежит ли предмет item гильдии guild, возвращает 1, если принадлежит, иначе - 0.
string Npc_GetNearestWP (c_npc self); - возвращает название WP, в которой расположен НПС self.
string Npc_GetNextWP (c_npc self); - возвращает название соседней точки WP, рядом с которой расположен НПС self.
int Npc_IsWayBlocked (c_npc self); - возвращает 1, если путь НПС self прегражден препятствием, иначе – 0.
int Npc_IsOnFP (c_npc self, string name); - проверяет, находится ли НПС self в точке FP с именем name, возвращает 1 – да, иначе – 0.
int Npc_IsDead (c_npc n0); - возвращает 1, если НПС n0 мертв, иначе – 0.
int Npc_KnowsInfo (c_npc self, int infoInstance); - возвращает TRUE, если НПС self уже получил информацию infoInstance, иначе FALSE.
int Npc_CheckInfo (c_npc npc, int important); - проверяет, имеет ли НПС npc действительную информацию для игрока, в этом случае возвращается 1 и начинается диалог, иначе 0, где important – важность информации (1 – важная, 0 – обычная).
int Npc_GiveInfo (c_npc npc, int important); - работает аналогично предыдущей функции, есть какое-то отличие, но я его не понял.
int Npc_CheckAvailableMission (c_npc npc, int missionState, int important); - проверяет, имеет ли npc миссию в состоянии missionState (AVAILABLE, RUNNING) с важностью important, возвращает 1, если имеет, иначе – 0.
int Npc_CheckRunningMission (c_npc npc, int important); - работает аналогично предыдущей функции, только проверяется миссия в состоянии текущего выполнения.
int Npc_CheckOfferMission (c_npc npc, int important); - работает аналогично предыдущей функции, только проверяется миссия в которой НПС npc может что-то предложить игроку.
int Npc_GetStateTime (c_npc self); - возвращает кол-во секунд, которые НПС self находится в текущем состоянии, заданном в " Loop " цикле.
void Npc_SetStateTime (c_npc self, int seconds); - установить кол-во секунд seconds, как долго НПС self может находится в этом состоянии.
int Npc_GetBodyState (c_npc self); - возвращает состояние, в котором находится НПС self (BS_константы).
int Npc_HasBodyFlag (c_npc self, int bodyFlag); - проверяет, установлен ли у НПС self флаг состояния bodyFlag, возвращает 1, если установлен, иначе – 0.
int Npc_IsPlayer (c_npc player); - возвращает 1, если проверяемый НПС player является ГГ.
int Npc_HasDetectedNpc (c_npc self, c_npc other); - возвращает 1, если НПС self чувствует (видит, сышит и т.д.) НПС other, иначе – 0.
int Npc_IsInState (c_npc self, func state); - запрос на текущее состояние фигуры НПС self, где state – функция состояния фигуры, возвращает TRUE, если НПС находится в этом состоянии, иначе – FALSE.
int WasInState (c_npc self, func state); - аналогично предыдущей функции, только запрос на предыдущее состояние фигуры НПС.
int Npc_IsInRoutine (c_npc self, func state); - проверяет, находится ли НПС self в функции state, возвращает TRUE если находится, иначе – FALSE.
void Npc_ExchangeRoutine (c_npc self, string routineName); - поменять у НПС self распорядок дня routineName.
int Npc_IsInCutscene (c_npc self); - возвращает 1, если НПС self находится во время проигрывания Cutscene, иначе – 0.
int Npc_IsVoiceActive (c_npc self); - возвращает 1, если НПС self разговаривает, иначе – 0.***
void Npc_ClearAIQueue (c_npc self); - удаляет все команды для НПС self из очереди AI_Queue.
void Npc_PlayAni (c_npc self, string file); - воспроизвести файл анимации file для НПС self.***
void Npc_SetRefuseTalk (c_npc self, int timeSec); - установить счетчик отказа от диалога для НПС self на timeSec секунд.
int Npc_RefuseTalk (c_npc self); - проверить, истек ли счетчик отказа от диалога для НПС self, возвращает TRUE, если счетчик не истек, иначе – FALSE.
int Npc_IsNear (c_npc self, c_npc other); - возвращает 1, если НПС other находится на расстоянии не менее 3 метров от НПС self, иначе – 0.
int Npc_GetDistToNpc (c_npc npc1, c_npc npc2); - возвращает расстояние в см. между npc1 и npc2.
int Npc_GetDistToWP (c_npc self, string wpName); - возвращает расстояние в см. между НПС self и WP wpName.
int Npc_GetDistToItem (c_npc self, c_item item); - возвращает расстояние в см. между НПС self и предметом item.
int Npc_GetDistToPlayer (c_npc npc1); - возвращает расстояние в см. между npc1 и ГГ.
void Npc_MemoryEntry (c_npc self, int source, c_npc offender, int newsid, c_npc victim); - данная функция записывает для НПС self новости, где source – источник новостей (определено два источника: NEWS_SOURCE_WITNESS – есть свидетель произошедшего, NEWS_SOURCE_GOSSIP – сплетня), newsid – идентификатор новости (определены новости: NEWS_MURDER - убийство, NEWS_ATTACK – атака, NEWS_THEFT – воровство, NEWS_DEFEAT – поражение, NEWS_NERVE – переживание, NEWS_INTERFERE – вмешательство, NEWS_HASDEFEATED – победа), offender – НПС преступник, victim – НПС жертва.
void Npc_MemoryEntryGuild (c_npc self, int source, c_npc offender, int newsid, c_npc victimguild); - работает аналогично предыдущей функции, только жертвой является вся гильдия.
int Npc_HasNews (c_npc self, int newsID, c_npc offender, c_npc victim); - эта функция проверяет, имеет ли НПС self новость с идентификатором newsID о жертве victim и преступнике offender, возвращает 1, если имеет, иначе – 0. (Вместо ненужных параметров можно записать 0).
int Npc_IsNewsGossip (c_npc self, int newsNumber); - возвращает 1, если новость для НПС self является сплетней, иначе – 0.
c_npc Npc_GetNewsWitness (c_npc self, int newsNumber); - возвращает ссылку на свидетеля в новости newsNumber для НПС self.
c_npc Npc_GetNewsVictim (c_npc self, int newsNumber); - возвращает ссылку на жертву в новости newsNumber для НПС self.
c_npc Npc_GetNewsOffender (c_npc self, int newsNumber); - возвращает ссылку на преступника в новости newsNumber для НПС self.
int Npc_DeleteNews (c_npc self, int newsNumber); - удаляет новость newsNumber для НПС self, возвращает 1, исли удаление успешно, иначе – 0.***
void Npc_ChangeAttribute (c_npc self, int atr, int value); - изменяет значение атрибута atr на кол-во единиц value для НПС self.
void Npc_CreateSpell (c_npc self, int spellnr); - НПС self создает заклинание spellnr, оно становится активным, но еще не применяется.
void Npc_LearnSpell (c_npc self, int spellnr); - НПС self выучил заклинание spellnr и может его использовать.
int Npc_GetActiveSpell (c_npc self); - возвращает номер заклинания, которое имеет активным НПС self, иначе - -1.
int Npc_GetActiveSpellCat (c_npc self); - возвращает номер категории активного заклинания у НПС self. Существуют три категории заклинаний:SPELL_GOOD, SPELL_NEUTRAL, SPELL_BAD.
int Npc_SetActiveSpellInfo (c_npc npc, int i1); - задает любое значение i1 для активного заклинания у НПС npc, это значение может использоваться в скриптах, экзешник на него не реагирует. Возвращаемое значение неизвестно.
int Npc_GetActiveSpellLevel (c_npc self); - возвращает уровень активного заклинания у НПС self.
int Npc_HasSpell (c_npc self, int spellID); - возвращает 1, если НПС self может использовать заклинание spellID, иначе – 0.
void Npc_PercEnable (c_npc self, int percID, func function); - функция активации восприятия percID у НПС self, где function – функция обработки восприятия.
void Npc_PercDisable (c_npc self, int percID); - функция деактивации восприятия percID у НПС self.
void Npc_SetPercTime (c_npc self, float seconds); - установка времени реакции в секундах seconds НПС self на событие для активного восприятия.
void Npc_SendPassivePerc (c_npc npc1, int Perc_type, c_npc npc2, c_npc npc3); - функция посылки пассивного восприятия Perc_type от npc1, где npc2 – жертва, npc3 – преступник.
void Npc_SendSinglePerc (c_npc self, c_npc target, int percID); - функция посылки восприятия percID от НПС self к НПС target.
void Npc_PerceiveAll (c_npc self); - разрешает НПС self воспринимать все объекты в зоне действия восприятия, затем можно использовать функции Wld_DetectNpc и Wld_DetectItem.
string Npc_GetDetectedMob (c_npc self); - возвращает имя MOB (Move Object) объекта, который распознал НПС self. Например: если имя MOB объекта " DOOR_OCR _135", то функция возвратит " DOOR ".
int Npc_CanSeeNpc (c_npc npc1, c_npc npc2); - возвращает TRUE, если npc1 может видеть npc2, иначе – FALSE.
int Npc_CanSeeNpcFreeLOS (c_npc self, c_npc other); - возвращает TRUE, если НПС self может видеть НПС other по прямой, без учета угла обзора, иначе – FALSE.
int Npc_CanSeeItem (c_npc npc1, c_item item); - возвращает TRUE, если npc1 может видеть предмет item, иначе – FALSE.
int Npc_CanSeeSource (c_npc self); - возвращает TRUE, если НПС self может видеть источник звука, иначе – FALSE.
int Npc_IsPlayerInMyRoom (c_npc npc); - возвращает TRUE, если НПС npc находится в своем помещении или в помещении своей гильдии, иначе – FALSE.
int Npc_WasPlayerInMyRoom (c_npc npc); - возвращает TRUE, если НПС npc находился в своем помещении или в помещении своей гильдии перед изменением своего положения, иначе – FALSE.
int Npc_GetComrades (c_npc npc); - возвращает 1, если НПС npc имеет друзей, иначе – 0. ***
int Npc_IsDetectedMobOwnedByNpc (c_npc user, c_npc owner); - возвращает значение > 0, если НПС owner является владельцем MOB а, который использует НПС user.
int Npc_IsDetectedMobOwnedByGuild (c_npc user, int ownerguild); - возвращает значение > 0, если гильдия ownerguild является владельцем MOB а, который использует НПС user.
void Npc_GiveItem (c_npc self, c_item item, c_npc other); - НПС self получает предмет item от НПС other.
int Npc_StartItemReactModules (c_npc self, c_npc other, c_item item); - проверяет все модули ItemReact реакции на предмет item, полученный НПС self от НПС other, запускает соответствующую функцию Reaction и возвращает TRUE, если находит модуль, иначе – FALSE.
int Npc_HasOffered (c_npc self, c_npc other, int itemInstance); - проверяет, имеет ли НПС other предмет itemInstance для передачи НПС self, возвращает TRUE, если предмет имеется, иначе – FALSE.
c_item Npc_GetInvItem (c_npc self, int itemInstance); - получить ссылку на предмет, который имеет НПС self с номером itemInstance.
int Npc_HasItems (c_npc n0, int itemInstance); - возвращает количетво предметов itemInstance у НПС n0.
int Npc_GetInvItemBySlot (c_npc self, int category, int slotNr); - возвращает кол- во предметов, которые находятся у НПС self, где category – категория инвентаря (INV_WEAPON, INV_ARMOR, INV_RUNE, INV_MAGIC, INV_FOOD, INV_POTION, INV_DOC, INV_MISC), slotNr – номер слота предмета.
void Npc_RemoveInvItem (c_npc owner, int itemInstance); - предмет itemInstance удаляется из инвентаря НПС owner и из игры.
void Npc_RemoveInvItems (c_npc owner, int itemInstance, int amount); - указанное кол-во amount предметов itemInstance удаляется из инвентаря НПС owner и из игры.
c_item Npc_GetEquippedMeleeWeapon (c_npc n0); - возвращает оружие ближнего радиуса поражения, которым экипирован НПС n0.
c_item Npc_GetEquippedRangedWeapon (c_npc n0); - возвращает оружие дальнего радиуса поражения, которым экипирован НПС n0.
c_item Npc_GetEquippedArmor (c_npc n0); - возвращает доспехи, которыми экипирован НПС n0.
int Npc_HasEquippedWeapon (c_npc self); - возвращает 1, если НПС self экипирован оружием, иначе - 0.
int Npc_HasEquippedMeleeWeapon (c_npc self); - возвращает 1, если НПС self экипирован оружием ближнего радиуса поражения, иначе - 0.
int Npc_HasEquippedRangedWeapon (c_npc self); - возвращает 1, если НПС self экипирован оружием дальнего радиуса поражения, иначе - 0.
int Npc_HasEquippedArmor (c_npc self); - возвращает 1, если НПС self экипирован доспехами, иначе - 0.
void Npc_SetToFistMode (c_npc self); - ставит НПС self в режим кулачного боя.
void Npc_SetToFightMode (c_npc self, int weapon); - ставит НПС self в режим боя с соответствующим оружием weapon.
int Npc_IsInFightMode (c_npc self, int fmode); - возвращает 1, если НПС self находится в боевом режиме fmode, иначе - 0. Заданы следующие боевые режимы: FMODE_NONE – небоевой режим, FMODE_FIST – режим кулачного боя, FMODE_MELEE – боевой режим с оружием ближнего радиуса поражения, FMODE_FAR - боевой режим с оружием дальнего радиуса поражения, FMODE_MAGIC – боевой режим с магией.
c_item Npc_GetReadiedWeapon (c_npc n0); - возвращает ссылку на оружие, которое НПС n0 держит в руке.
int Npc_HasReadiedWeapon (c_npc self); - возвращает 1, если НПС self держит любое оружие в руке, иначе - 0.
int Npc_HasReadiedMeleeWeapon (c_npc self); - возвращает 1, если НПС self держит в руке оружие ближнего радиуса поражения, иначе - 0.
int Npc_HasReadiedRangedWeapon (c_npc self); - возвращает 1, если НПС self держит в руке оружие дальнего радиуса поражения, иначе - 0.
int Npc_HasRangedWeaponWithAmmo (c_npc npc); - возвращает 1, если НПС npc держит в руке или имеет в инвентаре оружие дальнего радиуса поражения с боеприпасами, иначе - 0.
int Npc_GetTarget (c_npc self); - возвращает TRUE, если НПС self имеет цель для поражения (в качестве цели выступает НПС other), иначе – FALSE.
int Npc_GetNextTarget (c_npc self); - выполняется активный поиск цели для НПС self. Если цель находится, то она становится внутренней целью и записывается в переменную
other, если цель не найдена, то внутренняя цель удаляется и other становится недействительным. Критерий поиска цели - в качестве цели возмется враждебный противник, который не мертв или находится не в бессознательном состоянии. Возвращает TRUE, если цель найдена, иначе – FALSE. Внимание: Поиск основывается на активном восприятии НПС self, поэтому, если активное восприятие не установлено, то сначала следует применять функцию Npc_PerceiveAll ().
int Npc_IsNextTargetAvailable (c_npc self); - работает аналогично предыдущей функции, только ни внутренняя цель, ни переменная other не инициализируются.
void Npc_SetTarget (c_npc self, c_npc other); - устанавливает для НПС self в качестве внутренней цели для поражения НПС other.
int Npc_AreWeStronger (c_npc self, c_npc other); - выявляет более сильного НПС среди self и other по следующему алгоритму: если сумма уровней всех НПС (людей и монстров), которые враждебны к self и дружелюбны к other, более чем в два раза превышает сумму уровней всех НПС (людей и монстров), которые дружелюбны к self и враждебны к other, то other сльнее и возвращается FALSE, иначе сильнее self и возвращается TRUE. Замечания: 1. НПС, которые враждебны к обоим, не учитываются. 2. НПС, который враждебен к одному и дружелюбен к другому, будет участвовать в подсчете два раза.
int Npc_IsAiming (c_npc self, c_npc other); - возвращает 1, если НПС other целится в НПС self из оружия дальнего радиуса поражения или магией, иначе – 0.
int Npc_GetTalentSkill (c_npc self, int talent); - возвращает уровень навыка talent, который имеет НПС self.*** Определены следующие способности: NPC_TALENT _1 H – владение одноручником, NPC_TALENT _2 H – владение двуручником, NPC_TALENT_BOW – владение луком, NPC_TALENT_CROSSBOW – владение арбалетом, NPC_TALENT_PICKLOCK – умение вскрывать замки, NPC_TALENT_PICKPOCKET – карманная кража, NPC_TALENT_MAGE – владение магией, NPC_TALENT_SNEAK – умение подкрадываться, NPC_TALENT_REGENERATE – способность восстанавливать здоровье, NPC_TALENT_FIREMASTER – способность мастерски обращаться с огнем, NPC_TALENT_ACROBAT – акробатика.
int Npc_GetTalentValue (c_npc self, int talent); - возвращает кол-во единиц навыка talent, которые имеет НПС self.***
void Npc_SetTalentSkill (c_npc self, int talent, int level); - установить уровень level навыка talent, которым владеет НПС self.***
void Npc_SetTalentValue (c_npc self, int talent, int value); - установить кол-во единиц value навыка talent, которым владеет НПС self.***
Вот мы и закончили рассмотрение большого раздела – функции работы с НПС.

15. Функции искусственного интеллекта (AI_ функции).

void AI_Wait (c_npc n0, float n1); - НПС n0 переводится на n1 секунд в состояние ожидания, в этом состоянии он не делает ничего, но наносимый ущерб регистрируется и работает пассивное восприятие.
void AI_PlayAni (c_npc n0, string s0); - для НПС n0 проигрывается файл анимации s0 (имя файла s0 обязательно пишется заглавными буквами).
void AI_StandUp (c_npc self); - если НПС self находится в состоянии анимации, то проигрывается соответствующее движение, если НПС self находится на MOBSI объекте (стул, кровать и т.д.), то он встает.
void AI_StandUpQuick (c_npc self); - в отличии от предыдущей функции никаких движений не проигрывается, НПС self сразу переводится в стоячее состояние.
void AI_QuickLook (c_npc self, c_npc other); - НПС self быстро бросает взгляд на НПС other. Выполняется только движение головой в течение 2 секунд.
void AI_LookAt (c_npc self, string name); - НПС self смотрит на определенную точку на местности или на объект, где name – имя точки местности или имя VOB объекта.
void AI_LookAtNpc (c_npc self, c_npc other); - НПС self смотрит на НПС other.
void AI_StopLookAt (c_npc self); - НПС self снова смотрит прямо перед собой.
void AI_PointAt (c_npc self, string name); - НПС self указывает на определенную точку на местности или на объект, где name – имя точки местности или имя VOB объекта.
void AI_PointAtNpc (c_npc self, c_npc other); - НПС self указывает на НПС other.
void AI_StopPointAt (c_npc self); - НПС self прекращает указывать на что-нибудь или кого-нибудь.
void AI_TakeItem (c_npc self, c_item item); - НПС self берет (принимает) предмет item.
void AI_DropItem (c_npc self, int itemid); - НПС self выбрасывает на землю или на пол предмет itemid.
void AI_UseItem (c_npc self, int itemInstance); - НПС self использует предмет itemInstance.
void AI_UseItemToState (c_npc self, int itemInstance, int state); - НПС self использует предмет itemInstance до указанного состояния state (-1 – используется полностью).
void AI_TakeMob (c_npc self, string name); - НПС self берет (принимает) MOB (рюкзак, корзину и т.д.) с именем name.***
void AI_DropMob (c_npc self); - НПС self выбрасывает на землю или на пол имеющийся MOB.***
int AI_UseMob (c_npc self, string schemeName, int targetState); - НПС self использует MOB с именем schemeName до указанного состояния targetState. Если указанное состояние у MOBа уже имеется, НПС self приближается к нему, но ничего не делает.
void AI_Waitms (c_npc self, int msec); - НПС self переводится на msec миллисекунд в состояние ожидания, в этом состоянии он не делает ничего, но наносимый ущерб регистрируется и работает пассивное восприятие.***
void AI_CanSeeNpc (c_npc self, c_npc other, func see); - НПС self может увидеть НПС other, где see – какая-то функция (описания не нашел).***
void AI_SetWalkmode (c_npc n, int n0); - установить режим передвижения n0 для НПС n. Определены следующие режимы: NPC_RUN – бег, NPC_WALK – ходьба шагом, NPC_SNEAK – подкрадывание, NPC_RUN_WEAPON – бег с оружием, NPC_WALK_WEAPON – ходьба с оружием, NPC_SNEAK_WEAPON – подкрадывание с оружием.
void AI_GotoWP (c_npc n0, string s0); - НПС n0 перемещается в указанную WP s0.
void AI_GotoFP (c_npc self, string fpName); - НПС n0 перемещается в указанную FP fpName, расположенную в пределах 20 метров. Критерий поиска FP аналогичен используемому в функции Wld_IsFPAvailable.
void AI_GotoNextFP (c_npc self, string fpName); - работает аналогично предыдущей функции, только критерий поиска FP аналогичен используемому в функции Wld_IsNextFPAvailable.
void AI_GotoNpc (c_npc self, c_npc other); - НПС self перемещается к НПС other.
void AI_GotoItem (c_npc self, c_item item); - НПС self перемещается к предмету item.
void AI_GotoSound (c_npc n0); - НПС self перемещается к источнику звука.
void AI_Teleport (c_npc self, string waypoint); - телепортирует НПС self в новую локацию waypoint.
void AI_TurnToNpc (c_npc n0, c_npc n1); - НПС n0 поварачивается к НПС n1 лицом.
void AI_TurnAway (c_npc n0, c_npc n1); - НПС n0 поварачивается к НПС n1 спиной.
void AI_WhirlAround (c_npc self, c_npc other); - НПС self быстро поварачивается к НПС other лицом. (Делает оборот).
void AI_WhirlAroundToSource (c_npc self); - НПС self быстро поварачивается лицом к какому-то источнику.*** (Описания не нашел).
void AI_TurnToSound (c_npc self); - НПС self поварачивается лицом к источнику звука.
void AI_AlignToWP (c_npc self); - НПС self выравнивается в WP точке по направлению стрелки, заданной в Spacer.
void AI_AlignToFP (c_npc self); - НПС self выравнивается в FP точке по направлению стрелки, заданной в Spacer.***
void AI_Dodge (c_npc npc); - НПС npc отклоняется назад. (Способ защиты при атаке противника).
void AI_PlayAniBS (c_npc npc, string aniname, int bodystate); - для НПС npc проигрывается файл анимации aniname для определенного состояния тела bodystate.
void AI_PlayCutscene (c_npc self, string csName); - для НПС self проигрывается Cutscene с именем csName, заданная в скриптах.
void AI_DrawWeapon (c_npc n0); - НПС n0 вытаскивает оружие, которым экипирован.
void AI_RemoveWeapon (c_npc n0); - НПС n0 прячет оружие.
void AI_ReadyMeleeWeapon (c_npc self); - НПС self готовит оружие ближнего радиуса поражения к бою.
void AI_ReadyRangedWeapon (c_npc self); - НПС self готовит оружие дальнего радиуса поражения к бою.
void AI_Attack (c_npc self); - НПС self начинает сражение (эта функция должна вызываться внутри ZS_Attack_Loop). Атакуется внутренняя цель, которая была задана функцией Npc_SetTarget или Npc_GetNextTarget.
void AI_FinishingMove (c_npc self, c_npc other); - логическое завершение операции приближения (поворота) НПС self к НПС other.
void AI_Defend (c_npc self); - НПС self парирует удар противника (защищается). Выполняется только во время атаки противника.
void AI_Flee (c_npc self); - НПС self убегает от противника (эта функция должна вызываться внутри ZS_Loop). Предварительно функцией Npc_SetTarget должна быть установлена внутренняя цель, от которой НПС self должен убежать.
void AI_AimAt (c_npc attacker, c_npc target); - НПС attacker целится из оружия дальнего радиуса поражения в НПС target.
void AI_StopAim (c_npc attacker); - НПС attacker прекращает целиться из оружия дальнего радиуса поражения.
void AI_ShootAt (c_npc attacker, c_npc target); - НПС attacker стреляет из оружия дальнего радиуса поражения в НПС target.
void AI_CombatReactToDamage (c_npc self); - реакция НПС self на повреждение во время боя.***
void AI_LookForItem (c_npc self, int instance); - функция показывает определенный предмет instance, который имеется у НПС self. (Например: золотой меч разрушения).
void AI_EquipBestMeleeWeapon (c_npc self); - в инвентаре НПС self ищется самое лучшее оружие ближнего радиуса поражения и вешается на пояс.
void AI_EquipBestRangedWeapon (c_npc self); - в инвентаре НПС self ищется самое лучшее оружие дальнего радиуса поражения и вешается на спину.
void AI_EquipBestArmor (c_npc self); - в инвентаре НПС self ищутся и одеваются самые лучшие доспехи.
void AI_UnequipWeapons (c_npc self); - все экипированное оружие НПС self убирается в инвентарь.
void AI_UnequipArmor (c_npc self); - одетые доспехи НПС self убираются в инвентарь.
void AI_EquipArmor (c_npc self, int armor); - НПС self одевает доспехи armor, которые должны находиться в его инвентаре.***
void AI_ReadySpell (c_npc self, int spellID, int investMana); - НПС self берет в руку заклинание spellID со стоимостью маны investMana.
void AI_UnreadySpell (c_npc self); - НПС self прячет в инвентарь заклинание, которое имеет в руке.
void AI_Output (c_npc self, c_npc target, string outputName); - данная функция реализует диалог, НПС self говорит фразу outputName для НПС target. Текст фразы должен располагаться в скриптах в виде комментария к функции. (например: AI_Output () //текст фразы).
 void AI_OutputSVM (c_npc self, c_npc target, string svmname); - данная функция реализует SVM (Standart Voice Module) диалог, НПС self говорит фразу svmname для НПС target. Текст фразы должен располагаться в скриптах в файле svm.d (в одном из соответствующих SVM_ модулей) в виде комментария к строке с именем svmname. (например: StopMagic = svmname; //текст фразы).
 void AI_OutputSVM_Overlay (c_npc self, c_npc target, string svmname); -выполняется аналогично предыдущей функции, фраза выдается быстро, нет ожидания при разговоре следующей AI_ команды. Используется для комментариев перед сражением и во время сражения.
void AI_WaitTillEnd (c_npc self, c_npc other); - НПС self ждет от НПС other ответа на свою фразу. (Не выполняется перед функцией AI_OutputSVM_Overlay).
void AI_Ask (c_npc self, func answerYes, func answerNo); - определяет, как НПС self будет отвечать на сказанную фразу, при выборе ответа «Да» - выполняется функция answerYes, иначе – функция answerNo. Эти функции должны быть описаны заранее.
void AI_AskText (c_npc self, func funcYes, func funcNo, string strYes, string strNo); - работает аналогично предыдущей функции, только для вариантов ответов (кроме функций) могут быть заданы фразы ответов strYes, strNo.
void AI_WaitForQuestion (c_npc self, func scriptFunc); - НПС self ждет ответа или вопроса в течение 20 секунд, если в это время он поступает, то выполняется функция scriptFunc.
void AI_ProcessInfos (c_npc self); - эта функция запускает диалог для НПС self с возможностью выбора вариантов ответов/вопросов.***
void AI_StopProcessInfos (c_npc npc); - режим диалога НПС npc, начатый предыдущей функцией, заканчивается.
void AI_StartState (c_npc self, func what, int stateBehaviour, string wpName); - переводит НПС self в соответствующее состояние, описанное функцией what, где stateBehaviour = 0 – выполняется последовательность состояний, 1 – выполняется только функция конечного состояния, wpName – имя WP точки (если "", то состояние изменяется на месте расположения НПС).
void AI_SetNpcsToState (c_npc self, func aiStateFunc, int radius); - переводит всех НПС, находящихся от НПС self на расстоянии radius сантиметров, в соответствующее состояние, описанное функцией aiStateFunc.
void AI_ContinueRoutine (c_npc self); - продолжает выполнение распорядка дня НПС self.


Тема седьмая: Встроенные функции Готики 2.

В основном в Готике 2 используются функции Готики 1, но есть несколько дополнительных функций, их мы и рассмотрим.
int AI_PrintScreen (string msg, int posx, int posy, string font, int timeSec); - выводит на экран строку текста msg (имя шрифта – font) с координатами posx, posy (диапазон от 0 до 99% размера экрана, -1 означает вывод по центру соответствующей оси экрана) на время timeSec (в секундах).
void AI_Snd_Play (c_npc self, string s0); - воспроизвести звуковой файл с именем s0 для НПС self.
void AI_Snd_Play3D (c_npc self, c_npc n0, string s0); - воспроизвести звуковой файл с именем s0 для НПС self.
void AI_PlayFX (c_npc self, c_npc n0, string s0); - воспроизвести видео-файл с именем s0 для НПС self.
void AI_StopFX (c_npc self, string s0); - остановить воспроизведение видео-файла с именем s0 для НПС self.
int PlayVideoEx (string video, int aa, int bb); - показать видео файл video (c расширением BIK), возвращает 1, если видео показано, 0 – в случае ошибки. Трактование параметров aa и bb не знаю, в скриптах они имеют значение TRUE и FALSE.
void ExitSession () – используется вместо функции ExitGame (), хотя она тоже сохранена.
int GameInitGerman () – просто всегда возвращает 1.
В английской версии экзешника еще есть функции - int GameInitEnglish () и int GameInitEngintl (), их назначение мне неизвестно.
int Wld_DetectNpcExAtt (c_npc self, int instance, func aiState, int guild, int detectPlayer, int xxx); - эта функция инициализирует глобальную переменную скриптов other, отличную от НПС self, где instance – производная от класса c_npc, которая должна быть найдена и проинициализирована (-1 – любая производная), guild – гильдия, членом которой должен быть искомый НПС (-1 – любая гильдия), aiState – функция AI состояния, в котором должен находится искомый НПС (NOFUNC – любое AI состояние). Функция возвращает 1 в случае успешного завершения (other инициализирован найденным НПС), 0 – неудача (other не определен), параметр detectPlayer указывает, исключить ли игрока (ГГ) из поиска (0 – исключить, 1 – нет). В этой функции, по сравнению с Wld_DetectNpcEx, добавлен параметр ххх, к сожалению мне про него ничего не известно (возможно это какой-нибудь атрибут).
int Wld_IsRaining (); – возвращает 1, если идет дождь, иначе – 0.
void Wld_StopEffect (string name); - останавливает проигрывание эффекта с именем name, запущенного функцией Wld_PlayEffect.
void Doc_SetLevelCoords (int Document, int Left, int Top, int Right, int Bottom); - устанавливает границы области, отображаемой на карте (границы задаются в абсолютных координатах уровня), где: Document – обрабатываемый документ, Left, Top, Right и Bottom – задаваемые границы.
int Npc_GetLastHitSpellID (c_npc self); - возвращает номер заклинания, которое НПС self применил последним.
int Npc_GetLastHitSpellCat (c_npc self); - возвращает категорию заклинания, которое НПС self применил последним.
int Npc_GetActiveSpellIsScroll (c_npc self); - проверяет, является ли активное заклинание у НПС self свитком, если да – возвращает 1, иначе – 0.
void Npc_ClearInventory (c_npc self); - очищает весь инвентарь у НПС self.
int Npc_GetHeightToNpc (c_npc npc1, c_npc npc2); - возвращает расстояние в см. по высоте между НПС npc1 и npc2.
int Npc_GetHeightToItem (c_npc self, c_item item); - возвращает расстояние в см. по высоте между НПС self и предметом item.
c_npc Npc_GetLookAtTarget (c_npc self); - инициализирует глобальную переменную target ссылкой на НПС, на которого смотрит НПС self.
void Npc_StopAni (c_npc self, string name); - остановить проигрывание файла анимации с именем name для НПС self.
int Npc_IsDrawingWeapon (c_npc self); - возвращает номер оружия, которое НПС self держит в руках, иначе – 0.
int Npc_IsDrawingSpell (c_npc self); - возвращает номер заклинания, которое применяет НПС self, иначе – 0.
int Npc_IsInPlayersRoom (c_npc self); - возвращает 1, если НПС self находится в помещении, которое принадлежит ГГ, иначе – 0.
c_npc Npc_GetPortalOwner (c_npc self); - возвращает НПС-владельца помещения (области), в котором находится НПС self.
int Npc_GetPortal Guild (c_npc self); - возвращает номер гильдии владельца помещения (области), в котором находится НПС self.


Уроки скриптологии, часть 2.


Тема первая: Глобальные константы.

Практически все глобальные константы описаны в файле ..\_intern\constants.d
// Шрифты для функции PrintScreen
const string FONT_Screen            = "FONT_OLD_20_WHITE.TGA";    //обычный экранный шрифт
const string FONT_ScreenSmall       = "FONT_OLD_10_WHITE.TGA";    //мелкий экранный шрифт
const string FONT_Book              = "FONT_10_BOOK.TGA";         //шрифт текста документов
const string FONT_BookHeadline      = "FONT_20_BOOK.TGA";         //шрифт заголовков документов

// Стоимость маны для всех свитков заклинаний
const int SPL_Cost_Scroll           = 5;

// Индексы атрибутов НПС
const int ATR_HITPOINTS             = 0;    //Текущая жизнь
const int ATR_HITPOINTS_MAX         = 1;    //Макс. жизнь
const int ATR_MANA                  = 2;    //Текущая мана
const int ATR_MANA_MAX              = 3;    //Макс. мана
const int ATR_STRENGTH              = 4;    //Сила
const int ATR_DEXTERITY             = 5;    //Ловкость
const int ATR_REGENERATEHP          = 6;    //Регенерация жизни в секунду
const int ATR_REGENERATEMANA        = 7;    //Регенерация маны в секунду

const int ATR_INDEX_MAX             = 8;    //Макс. размерность массива атрибутов НПС

// Флаги НПС
const int NPC_FLAG_FRIEND           = 1 << 0;      //Друг (не используется, заменен на aivar[])
const int NPC_FLAG_IMMORTAL         = 1 << 1;      //Неуязвимый
const int NPC_FLAG_GHOST            = 1 << 2;      //Полупрозрачный (эквивалентен флагу 'GhostAlpha' в Gothic.ini секция [INTERNAL])

// Режимы борьбы
const int FMODE_NONE                = 0;           //Выключен
const int FMODE_FIST                = 1;           //Кулачный бой
const int FMODE_MELEE               = 2;           //Оружие ближнего радиуса поражения
const int FMODE_FAR                 = 5;           //Оружие дальнего радиуса поражения
const int FMODE_MAGIC               = 7;           //Магия

// Режимы передвижения
const int NPC_RUN                   = 0;           //Бег
const int NPC_WALK                  = 1;           //Ходьба
const int NPC_SNEAK                 = 2;           //Подкрадывание
const int NPC_RUN_WEAPON            = 0 + 128;     //Бег с оружием
const int NPC_WALK_WEAPON           = 1 + 128;     //Ходьба с оружием
const int NPC_SNEAK_WEAPON          = 2 + 128;     //Подкрадывание с оружием

// Флаги брони
const int WEAR_TORSO                = 1;           //Верхняя часть туловища (Доспехи)
const int WEAR_HEAD                 = 2;           //Голова (Шлем)
const int WEAR_EFFECT               = 16;          //Оказывает эффект при ношении

// Категории инвентаря
const int INV_WEAPON                = 1;           //Оружие
const int INV_ARMOR                 = 2;           //Броня
const int INV_RUNE                  = 3;           //Руны
const int INV_MAGIC                 = 4;           //Магия
const int INV_FOOD                  = 5;           //Продукты питания
const int INV_POTION                = 6;           //Напитки
const int INV_DOC                   = 7;           //Документы
const int INV_MISC                  = 8;           //Разное
const int INV_CAT_MAX               = 9;           //Макс. размерность категорий инвентаря

// Вместимость инвентаря (программой игнорируется, инвентарь неограниченный!)
const int INV_MAX_WEAPONS           = 6;
const int INV_MAX_ARMORS            = 2;
const int INV_MAX_RUNES             = 1000;
const int INV_MAX_FOOD              = 15;
const int INV_MAX_DOCS              = 1000;
const int INV_MAX_POTIONS           = 1000;
const int INV_MAX_MAGIC             = 1000;
const int INV_MAX_MISC              = 1000;

// Кол-во строк текста описания предметов
const int ITM_TEXT_MAX              = 6;

// Флаги предметов
// (категории)
const int ITEM_KAT_NONE             = 1 <<  0;          //Прочее
const int ITEM_KAT_NF               = 1 <<  1;          //Оружие ближнего радиуса поражения
const int ITEM_KAT_FF               = 1 <<  2;          //Оружие дальнего радиуса поражения
const int ITEM_KAT_MUN              = 1 <<  3;          //Боеприпасы (мульти)
const int ITEM_KAT_ARMOR            = 1 <<  4;          //Броня
const int ITEM_KAT_FOOD             = 1 <<  5;          //Продукты питания (мульти)
const int ITEM_KAT_DOCS             = 1 <<  6;          //Документы
const int ITEM_KAT_POTIONS          = 1 <<  7;          //Напитки
const int ITEM_KAT_LIGHT            = 1 <<  8;          //Источники света
const int ITEM_KAT_RUNE             = 1 <<  9;          //Руны/Свитки заклинаний
const int ITEM_KAT_MAGIC            = 1 << 31;          //Кольца/Амулеты/Пояса
const int ITEM_KAT_KEYS             = ITEM_KAT_NONE;    //Ключи
// (типы оружия)
const int ITEM_DAG                  = 1 << 13;          //Клинок (не используется)
const int ITEM_SWD                  = 1 << 14;          //Меч
const int ITEM_AXE                  = 1 << 15;          //Топор
const int ITEM_2HD_SWD              = 1 << 16;          //Друручный меч
const int ITEM_2HD_AXE              = 1 << 17;          //Двуручный топор
const int ITEM_SHIELD               = 1 << 18;          //Щит (не используется)
const int ITEM_BOW                  = 1 << 19;          //Лук
const int ITEM_CROSSBOW             = 1 << 20;          //Арбалет
// (типы магических предметов)
const int ITEM_RING                  = 1 << 11;          //Кольцо
const int ITEM_AMULET                = 1 << 22;          //Амулет
const int ITEM_BELT                  = 1 << 24;          //Пояс
// (атрибуты предметов)
const int ITEM_DROPPED               = 1 << 10;          //Выброшенный предмет (внутренний флаг!)
const int ITEM_MISSION               = 1 << 12;          //Предмет миссии
const int ITEM_MULTI                 = 1 << 21;          //Складываемый по несколько штук
const int ITEM_NFOCUS                = 1 << 23;          //Не в фокусе (внутренний флаг!)
const int ITEM_CREATEAMMO            = 1 << 25;          //Боеприпасы производятся сами (магически)
const int ITEM_NSPLIT                = 1 << 26;          //Неделимый предмет
const int ITEM_DRINK                 = 1 << 27;          //Можно пить (не используется)
const int ITEM_TORCH                 = 1 << 28;          //Факел
const int ITEM_THROW                 = 1 << 29;          //Можно бросить (не используется)
const int ITEM_ACTIVE                = 1 << 30;          //Активный (внутренний флаг!)
// Типы наносимых повреждений
const int DAM_INVALID                = 0;                    //(применения не нашел)
const int DAM_BARRIER                = 1;                    //(применения не нашел)
const int DAM_BLUNT                  = DAM_BARRIER  << 1;    //Повреждение тупым оружием (молоты, дубины, кулаки)
const int DAM_EDGE                   = DAM_BLUNT    << 1;    //Повреждение острым оружием (мечи, шпаги, топоры)
const int DAM_FIRE                   = DAM_EDGE     << 1;    //Повреждение огнем
const int DAM_FLY                    = DAM_FIRE     << 1;    //Повреждение нокаутом (после удара НПС отлетает и падает)
const int DAM_MAGIC                  = DAM_FLY      << 1;    //Повреждение магией
const int DAM_POINT                  = DAM_MAGIC    << 1;    //Точечное повреждение (луки, арбалеты)
const int DAM_FALL                   = DAM_POINT    << 1;    //Повреждение от падения

// Индексы значений массива наносимых повреждений (имена индексов соответствуют именам типов)
const int DAM_INDEX_BARRIER          = 0;
const int DAM_INDEX_BLUNT            = DAM_INDEX_BARRIER + 1;
const int DAM_INDEX_EDGE             = DAM_INDEX_BLUNT   + 1;
const int DAM_INDEX_FIRE             = DAM_INDEX_EDGE    + 1;
const int DAM_INDEX_FLY              = DAM_INDEX_FIRE    + 1;
const int DAM_INDEX_MAGIC            = DAM_INDEX_FLY     + 1;
const int DAM_INDEX_POINT            = DAM_INDEX_MAGIC   + 1;
const int DAM_INDEX_FALL             = DAM_INDEX_POINT   + 1;
const int DAM_INDEX_MAX              = DAM_INDEX_FALL    + 1;    //Макс. размерность массива наносимых повреждений

// Другие константы наносимых повреждений
const int NPC_ATTACK_FINISH_DISTANCE                = 180;       //???
const int NPC_BURN_TICKS_PER_DAMAGE_POINT           = 1000;      //???
const int NPC_BURN_DAMAGE_POINTS_PER_INTERVALL      = 50;        //???
const int DAM_CRITICAL_MULTIPLIER                   = 2;         //Коэффициент умножения для критического повреждения (не употребляется)

const int BLOOD_SIZE_DIVISOR                        = 1000;      //???
const int BLOOD_DAMAGE_MAX                          = 200;       //???

const int DAMAGE_FLY_CM_MAX                         = 2000;      //???
const int DAMAGE_FLY_CM_MIN                         = 300;       //???
const int DAMAGE_FLY_CM_PER_POINT                   = 5;         //???

const int NPC_DAM_DIVE_TIME                         = 100;       //???

const int IMMUNE                                    = -1;        //Иммунитет к повреждению

// На каком расстоянии НПС перестают сражаться
// Коэффициент к дистанции (BASE + дистанция поражения оружием)
const float NPC_COLLISION_CORRECTION_SCALER         = 0.75;

//      Типы защиты (имена соответствуют типам наносимых повреждений)
const int PROT_BARRIER               = DAM_INDEX_BARRIER;
const int PROT_BLUNT                 = DAM_INDEX_BLUNT;
const int PROT_EDGE                  = DAM_INDEX_EDGE;
const int PROT_FIRE                  = DAM_INDEX_FIRE;
const int PROT_FLY                   = DAM_INDEX_FLY;
const int PROT_MAGIC                 = DAM_INDEX_MAGIC;
const int PROT_POINT                 = DAM_INDEX_POINT;
const int PROT_FALL                  = DAM_INDEX_FALL;
const int PROT_INDEX_MAX             = DAM_INDEX_MAX;      //Макс. размерность массива защиты

// Активные восприятия
const int PERC_ASSESSPLAYER          = 1;      //Восприятие ГГ
const int PERC_ASSESSENEMY           = 2;      //Восприятие врага
const int PERC_ASSESSFIGHTER         = 3;      //Восприятие в сражении
const int PERC_ASSESSBODY            = 4;      //Восприятие тела
const int PERC_ASSESSITEM            = 5;      //Восприятие предмета

// Чувства
const int SENSE_SEE                  = 1 << 0;      //Зрение
const int SENSE_HEAR                 = 1 << 1;      //Слух
const int SENSE_SMELL                = 1 << 2;      //Обоняние

// Пассивные восприятия
const int PERC_ASSESSMURDER          = 6;       //Восприятие убийства
const int PERC_ASSESSDEFEAT          = 7;       //Восприятие поражения
const int PERC_ASSESSDAMAGE          = 8;       //Восприятие повреждения
const int PERC_ASSESSOTHERSDAMAGE    = 9;       //Восприятие других повреждений
const int PERC_ASSESSTHREAT          = 10;      //Восприятие угрозы
const int PERC_ASSESSREMOVEWEAPON    = 11;      //Восприятие от "зачехления" оружия
const int PERC_OBSERVEINTRUDER       = 12;      //Восприятие наблюдения злоумышленника
const int PERC_ASSESSFIGHTSOUND      = 13;      //Восприятие звука сражения
const int PERC_ASSESSQUIETSOUND      = 14;      //Восприятие тихих звуков
const int PERC_ASSESSWARN            = 15;      //Восприятие предупреждения
const int PERC_CATCHTHIEF            = 16;      //Восприятие выгоды воровства
const int PERC_ASSESSTHEFT           = 17;      //Восприятие воровства
const int PERC_ASSESSCALL            = 18;      //Восприятие обращения (речь)
const int PERC_ASSESSTALK            = 19;      //Восприятие разговора
const int PERC_ASSESSGIVENITEM       = 20;      //Восприятие торговли
const int PERC_ASSESSFAKEGUILD       = 21;      //Восприятие "поддельной гильдии" (посылается при превращении НПС)
const int PERC_MOVEMOB               = 22;      //Восприятие передвижения объекта (не НПС)
const int PERC_MOVENPC               = 23;      //Восприятие передвижения НПС
const int PERC_DRAWWEAPON            = 24;      //Восприятие вытаскивания оружия
const int PERC_OBSERVESUSPECT        = 25;      //Восприятие наблюдения подозреваемого
const int PERC_NPCCOMMAND            = 26;      //Восприятие команды НПС
const int PERC_ASSESSMAGIC           = 27;      //Восприятие магии
const int PERC_ASSESSSTOPMAGIC       = 28;      //Восприятие прекращения колдовства
const int PERC_ASSESSCASTER          = 29;      //Восприятие колдующего (посылается при изменении маны)
const int PERC_ASSESSSURPRISE        = 30;      //Восприятие неожиданности
const int PERC_ASSESSENTERROOM       = 31;      //Восприятие входа в помещение (область)
const int PERC_ASSESSUSEMOB          = 32;      //Восприятие использования объекта (открытие двери, сундука)

// Способы распространения новостей
const int NEWS_DONT_SPREAD                             = 0;      //Новости не распространяются
const int NEWS_SPREAD_NPC_FRIENDLY_TOWARDS_VICTIM      = 1;      //Друзьями жертвы
const int NEWS_SPREAD_NPC_FRIENDLY_TOWARDS_WITNESS     = 2;      //Друзьями свидетеля
const int NEWS_SPREAD_NPC_FRIENDLY_TOWARDS_OFFENDER    = 3;      //Друзьями обидчика
const int NEWS_SPREAD_NPC_SAME_GUILD_VICTIM            = 4;      //Гильдией жертвы

// Константы новостей
const int IMPORTANT                  = 1;      //Важная новость

// Статусы информации
const int INF_TELL                   = 0;      //Сказанная информация
const int INF_UNKNOWN                = 2;      //Источник информации неизвестен

// Статусы миссий
const int LOG_RUNNING                = 1;      //Миссия выполняется
const int LOG_SUCCESS                = 2;      //Миссия успешно завершена
const int LOG_FAILED                 = 3;      //Миссия провалена
const int LOG_OBSOLETE               = 4;      //Миссия завершена в другом направлении (был выбор вариантов завершения)

// Отношения
const int ATT_FRIENDLY               = 3;      //Друг
const int ATT_NEUTRAL                = 2;      //Нейтральный
const int ATT_ANGRY                  = 1;      //Сердитый, злой
const int ATT_HOSTILE                = 0;      //Враг

// Гильдии
const int GIL_NONE                   = 0;      //Отсутствует
const int GIL_HUMAN                  = 1;      //Люди (специальная гильдия, набор констант для этой гильдии описан в файле Species.d)
const int GIL_PAL                    = 1;      //Паладины
const int GIL_MIL                    = 2;      //Милиционеры
const int GIL_VLK                    = 3;      //Горожане
const int GIL_KDF                    = 4;      //Маги огня
const int GIL_NOV                    = 5;      //Маги послушники
const int GIL_DJG                    = 6;      //Охотники на драконов
const int GIL_SLD                    = 7;      //Ополчение
const int GIL_BAU                    = 8;      //Крестьяне
const int GIL_BDT                    = 9;      //Бандиты
const int GIL_STRF                   = 10;     //Заключенные
const int GIL_DMT                    = 11;     //Черные маги
const int GIL_OUT                    = 12;     //Чужеземцы
const int GIL_PIR                    = 13;     //Пираты
const int GIL_KDW                    = 14;     //Маги воды
const int GIL_EMPTY_D                = 15;     //(пусто)
//-----------------------------------------------
const int GIL_PUBLIC                 = 15;     //Публичная гильдия
//-----------------------------------------------

const int GIL_SEPERATOR_HUM          = 16;     //Разделитель между людьми и животными

const int GIL_MEATBUG                         = 17;      //Мясной жук
const int GIL_SHEEP                           = 18;      //Овца
const int GIL_GOBBO                           = 19;      //Гоблин / Черный гоблин
const int GIL_GOBBO_SKELETON                  = 20;      //Скелет гоблина
const int GIL_SUMMONED_GOBBO_SKELETON         = 21;      //Вызванный Скелет гоблина
const int GIL_SCAVENGER                       = 22;      //Падальщик / Зубастик
const int GIL_GIANT_RAT                       = 23;      //Крыса
const int GIL_GIANT_BUG                       = 24;      //Полевой жук
const int GIL_BLOODFLY                        = 25;      //Кровяной шершень
const int GIL_WARAN                           = 26;      //Ящерица / Огненная ящерица
const int GIL_WOLF                            = 27;      //Волк / Варг
const int GIL_SUMMONED_WOLF                   = 28;      //Вызванный Волк
const int GIL_MINECRAWLER                     = 29;      //Ползун / Воин ползунов
const int GIL_LURKER                          = 30;      //Шныг
const int GIL_SKELETON                        = 31;      //Скелет
const int GIL_SUMMONED_SKELETON               = 32;      //Вызванный Скелет
const int GIL_SKELETON_MAGE                   = 33;      //Маг скелетов
const int GIL_ZOMBIE                          = 34;      //Зомби
const int GIL_SNAPPER                         = 35;      //Глорх / Остер / Расчленитель
const int GIL_SHADOWBEAST                     = 36;      //Мракорис
const int GIL_SHADOWBEAST_SKELETON            = 37;      //Скелет мракориса
const int GIL_HARPY                           = 38;      //Гарпия
const int GIL_STONEGOLEM                      = 39;      //Каменный голем
const int GIL_FIREGOLEM                       = 40;      //Огненный голем
const int GIL_ICEGOLEM                        = 41;      //Ледяной голем
const int GIL_SUMMONED_GOLEM                  = 42;      //Вызванный Голем
const int GIL_DEMON                           = 43;      //Демон / Лорд демонов
const int GIL_SUMMONED_DEMON                  = 44;      //Вызванный Демон
const int GIL_TROLL                           = 45;      //Троль / Черный троль
const int GIL_SWAMPSHARK                      = 46;      //Болотожор
const int GIL_DRAGON                          = 47;      //Дракон
const int GIL_MOLERAT                         = 48;      //Кротокрыс

const int GIL_ALLIGATOR                       = 49;      //Алигатор
const int GIL_SWAMPGOLEM                      = 50;      //Болотный голем
const int GIL_Stoneguardian                   = 51;      //Каменный сторож
const int GIL_Gargoyle                        = 52;      //Пантера / Огненный дух мракориса
const int GIL_Empty_A                         = 53;      //(пусто)
const int GIL_SummonedGuardian                = 54;      //Вызванный Каменный сторож
const int GIL_SummonedZombie                  = 55;      //Вызванный Зомби
const int GIL_EMPTY_B                         = 56;      //(пусто)
const int GIL_EMPTY_C                         = 57;      //(пусто)

const int GIL_SEPERATOR_ORC                   = 58;      //Разделитель животных от орков

const int GIL_ORC                             = 59;      //Орк / Шаман орков / Воин орков
const int GIL_FRIENDLY_ORC                    = 60;      //Ур-Шак
const int GIL_UNDEADORC                       = 61;      //Орк нежить
const int GIL_DRACONIAN                       = 62;      //Ящер

const int GIL_EMPTY_X                         = 63;      //(пусто)
const int GIL_EMPTY_Y                         = 64;      //(пусто)
const int GIL_EMPTY_Z                         = 65;      //(пусто)

const int GIL_MAX                             = 66;      //Макс. количество гильдий

// Класс описания гильдий
class c_gilvalues
{
     var int            WATER_DEPTH_KNEE           [GIL_MAX];      //Глубина воды до колена
     var int            WATER_DEPTH_CHEST          [GIL_MAX];      //Глубина воды до груди
     var int            JUMPUP_HEIGHT              [GIL_MAX];      //Высота прыжка
//     var int            JUMPUP_FORCE               [GIL_MAX];
     var int            SWIM_TIME                  [GIL_MAX];      //Время плавания
     var int            DIVE_TIME                  [GIL_MAX];      //Время пребывания под водой
     var int            STEP_HEIGHT                [GIL_MAX];      //Ширина шага
     var int            JUMPLOW_HEIGHT             [GIL_MAX];      //Высота низкого прыжка
     var int            JUMPMID_HEIGHT             [GIL_MAX];      //Высота среднего прыжка
     var int            SLIDE_ANGLE                [GIL_MAX];      //Угол начала скольжения на наклонной плоскости
     var int            SLIDE_ANGLE2               [GIL_MAX];      //???
     var int            DISABLE_AUTOROLL           [GIL_MAX];      //???
     var int            SURFACE_ALIGN              [GIL_MAX];      //???
     var int            CLIMB_HEADING_ANGLE        [GIL_MAX];      //Какие-то углы подъема
     var int            CLIMB_HORIZ_ANGLE          [GIL_MAX];      //
     var int            CLIMB_GROUND_ANGLE         [GIL_MAX];      //
     var int            FIGHT_RANGE_BASE           [GIL_MAX];      //Какие-то диапазоны борьбы
     var int            FIGHT_RANGE_FIST           [GIL_MAX];      //
     var int            FIGHT_RANGE_G              [GIL_MAX];      //
     var int            FIGHT_RANGE_1HS            [GIL_MAX];      //
     var int            FIGHT_RANGE_1HA            [GIL_MAX];      //
     var int            FIGHT_RANGE_2HS            [GIL_MAX];      //
     var int            FIGHT_RANGE_2HA            [GIL_MAX];      //
     var int            FALLDOWN_HEIGHT            [GIL_MAX];      //Высота падения без повреждения
     var int            FALLDOWN_DAMAGE            [GIL_MAX];      //Повреждение от падения с высоты
     var int            BLOOD_DISABLED             [GIL_MAX];      //Уровень показа крови
     var int            BLOOD_MAX_DISTANCE         [GIL_MAX];      //Макс. радиус брызг крови в см.
     var int            BLOOD_AMOUNT               [GIL_MAX];      //Количество крови
     var int            BLOOD_FLOW                 [GIL_MAX];      //???
     var string         BLOOD_EMITTER              [GIL_MAX];      //Источник крови
     var string         BLOOD_TEXTURE              [GIL_MAX];      //Текстура крови
     var int            TURN_SPEED                 [GIL_MAX];      //Скорость разворота
};

// Типы звуков
const int NPC_SOUND_DROPTAKE         = 1;      //Звук капель
const int NPC_SOUND_SPEAK            = 3;      //Звук разговора
const int NPC_SOUND_STEPS            = 4;      //Звук шагов
const int NPC_SOUND_THROWCOLL        = 5;      //Звук летящей стрелы, болта
const int NPC_SOUND_DRAWWEAPON       = 6;      //Звук доставаемого оружия
const int NPC_SOUND_SCREAM           = 7;      //Звук крика
const int NPC_SOUND_FIGHT            = 8;      //Звук сражения

// Типы материалов
const int MAT_WOOD                   = 0;      //Деревянный
const int MAT_STONE                  = 1;      //Каменный
const int MAT_METAL                  = 2;      //Металлический
const int MAT_LEATHER                = 3;      //Кожаный
const int MAT_CLAY                   = 4;      //Глиняный
const int MAT_GLAS                   = 5;      //Стеклянный

// Записи дневника
const int LOG_MISSION                = 0;      //Миссии
const int LOG_NOTE                   = 1;      //Заметки

// Другие константы
const int TIME_INFINITE                      = -1000000 / 1000;   //Бесконечное время ???
const int NPC_VOICE_VARIATION_MAX            = 10;                //Макс. кол-во голосов НПС

const float TRADE_VALUE_MULTIPLIER           = 0.15;              //Коэффициент стоимости предмета, которую платит торговец
const string TRADE_CURRENCY_INSTANCE         = "ITMI_GOLD";       //Имя валюты для расчетов
//************************************
// Заклинания
//************************************

// Константы категорий
const int SPELL_GOOD            = 0;    //Хорошее
const int SPELL_NEUTRAL         = 1;    //Нейтральное
const int SPELL_BAD             = 2;    //Плохое

// Константы управления
const int SPL_DONTINVEST                    = 0;    //Дальнейшее инвестирование маны на зарядку заклинания невозможно (мало или нет маны)
const int SPL_RECEIVEINVEST                 = 1;    //Нужное количество маны израсходовано на зарядку (заклинание готово к действию)
const int SPL_SENDCAST                      = 2;    //Старт заклинания с действием
const int SPL_SENDSTOP                      = 3;    //Заклинание заканчивается без эффекта (мало маны)
const int SPL_NEXTLEVEL                     = 4;    //Переход на следующий уровень зарядки заклинания
const int SPL_STATUS_CANINVEST_NO_MANADEC   = 8;    //Инвестированного количества маны недостаточно для перехода на следующий уровень
const int SPL_FORCEINVEST             = 1 << 16;    //Какой-то флаг управляющий инвестированием маны ???

// Константы цели
const int TARGET_COLLECT_NONE                   = 0;    //Цель определяется самим заклинанием
const int TARGET_COLLECT_CASTER                 = 1;    //Цель - кастующий
const int TARGET_COLLECT_FOCUS                  = 2;    //Цель - визуальный объект в фокусе
const int TARGET_COLLECT_ALL                    = 3;    //Цель - все объекты в радиусе поражения
const int TARGET_COLLECT_FOCUS_FALLBACK_NONE    = 4;    //Цель - визуальный объект в фокусе, при потере фокуса траектория устанавливается заклинанием
const int TARGET_COLLECT_FOCUS_FALLBACK_CASTER  = 5;    //Цель - визуальный объект в фокусе, при потере фокуса целью становится кастующий
const int TARGET_COLLECT_ALL_FALLBACK_NONE      = 6;    //Цель - все объекты в радиусе поражения, если объектов нет траектория устанавливается заклинанием
const int TARGET_COLLECT_ALL_FALLBACK_CASTER    = 7;    //Цель - все объекты в радиусе поражения, если объектов нет целью становится кастующий

const int TARGET_TYPE_ALL       = 1;    //Цель - все и всё
const int TARGET_TYPE_ITEMS     = 2;    //Цель - предмет
const int TARGET_TYPE_NPCS      = 4;    //Цель - НПС
const int TARGET_TYPE_ORCS      = 8;    //Цель - орк
const int TARGET_TYPE_HUMANS    = 16;   //Цель - человек
const int TARGET_TYPE_UNDEAD    = 32;   //Цель - неуязвимый

// Идентификаторы заклинаний (ID)
// Руны Паладинов
const int SPL_PalLight              = 0;    //Святой свет
const int SPL_PalLightHeal          = 1;    //Малое лечение
const int SPL_PalHolyBolt           = 2;    //Святая стрела
const int SPL_PalMediumHeal         = 3;    //Среднее лечение
const int SPL_PalRepelEvil          = 4;    //Разогнать дьяволов
const int SPL_PalFullHeal           = 5;    //Сильное лечение
const int SPL_PalDestroyEvil        = 6;    //Уничтожить дьяволов

// Руны Телепортации
const int SPL_PalTeleportSecret     = 7;    //Телепорт
const int SPL_TeleportSeaport       = 8;    //Телепорт в гавань
const int SPL_TeleportMonastery     = 9;    //Телепорт к монастырю
const int SPL_TeleportFarm          = 10;   //Телепорт к помещику
const int SPL_TeleportXardas        = 11;   //Телепорт к Ксардасу
const int SPL_TeleportPassNW        = 12;   //Телепорт к проходу в Хоринисе
const int SPL_TeleportPassOW        = 13;   //Телепорт к проходу в рудниковой долине
const int SPL_TeleportOC            = 14;   //Телепорт в замок
const int SPL_TeleportOWDemonTower  = 15;   //Телепорт в старую башню демонов
const int SPL_TeleportTaverne       = 16;   //Телепорт к таверне
const int SPL_Teleport_3            = 17;   //(не используется)

// Круг 1
const int SPL_Light                 = 18;   //Свет
const int SPL_Firebolt              = 19;   //Огненная стрела
const int SPL_LightHeal             = 21;   //Лечить легкое ранение
const int SPL_SummonGoblinSkeleton  = 22;   //Вызвать скелет гоблина
const int SPL_Zap                   = 24;   //Молния

// Круг 2
const int SPL_Icebolt               = 20;   //Ледяная стрела
const int SPL_InstantFireball       = 23;   //Огненный шар
const int SPL_SummonWolf            = 25;   //Вызвать волка
const int SPL_WindFist              = 26;   //Порыв ветра
const int SPL_Sleep                 = 27;   //Сон

// Круг 3
const int SPL_MediumHeal            = 28;   //Лечить среднее ранение
const int SPL_LightningFlash        = 29;   //Удар молнии
const int SPL_ChargeFireball        = 30;   //Большой огненный шар
const int SPL_SummonSkeleton        = 31;   //Вызвать скелет
const int SPL_Fear                  = 32;   //Страх
const int SPL_IceCube               = 33;   //Ледяная глыба

// Круг 4
const int SPL_ChargeZap             = 34;   //Шаровая молния
const int SPL_SummonGolem           = 35;   //Вызвать голема
const int SPL_DestroyUndead         = 36;   //Уничтожить нежить
const int SPL_Pyrokinesis           = 37;   //Большая огненная буря (ошибка перевода ???)

// Круг 5
const int SPL_Firestorm             = 38;   //Малая огненная буря (ошибка перевода ???)
const int SPL_IceWave               = 39;   //Ледяная волна
const int SPL_SummonDemon           = 40;   //Вызвать демона
const int SPL_FullHeal              = 41;   //Лечить сильное ранение

// Круг 6
const int SPL_Firerain              = 42;   //Огненный дождь
const int SPL_BreathOfDeath         = 43;   //Дым смерти
const int SPL_MassDeath             = 44;   //Волна смерти
const int SPL_ArmyOfDarkness        = 45;   //Армия мрака
const int SPL_Shrink                = 46;   //Уменьшить монстра

// Свитки
const int SPL_TrfSheep              = 47;   //Превращение в овцу
const int SPL_TrfScavenger          = 48;   //Превращение в падальщика
const int SPL_TrfGiantRat           = 49;   //Превращение в крысу
const int SPL_TrfGiantBug           = 50;   //Превращение в полевого жука
const int SPL_TrfWolf               = 51;   //Превращение в волка
const int SPL_TrfWaran              = 52;   //Превращение в ящерицу
const int SPL_TrfSnapper            = 53;   //Превращение в глорха
const int SPL_TrfWarg               = 54;   //Превращение в варга
const int SPL_TrfFireWaran          = 55;   //Превращение в огненную ящерицу
const int SPL_TrfLurker             = 56;   //Превращение в шныга
const int SPL_TrfShadowbeast        = 57;   //Превращение в мракориса
const int SPL_TrfDragonSnapper      = 58;   //Превращение в остера
const int SPL_Charm                 = 59;   //Забыть

// Круг 5
const int SPL_MasterOfDisaster      = 60;   //Святой удар

// ???
const int SPL_Deathbolt             = 61;   //Стрела смерти
const int SPL_Deathball             = 62;   //Шар смерти
const int SPL_ConcussionBolt        = 63;   //Стрела сотрясения
const int SPL_Reserved_64           = 64;   //
const int SPL_Reserved_65           = 65;   //
const int SPL_Reserved_66           = 66;   //
const int SPL_Reserved_67           = 67;   //
const int SPL_Reserved_68           = 68;   //
const int SPL_Reserved_69           = 69;   //

// Заклинания магов воды
const int SPL_Thunderstorm          = 70;   //Шторм
const int SPL_Whirlwind             = 71;   //Смерч
const int SPL_WaterFist             = 72;   //Кулак воды
const int SPL_IceLance              = 73;   //Ледяное копье
const int SPL_Inflate               = 74;   //Раздуть
const int SPL_Geyser                = 75;   //Гейзер
const int SPL_Waterwall             = 76;   //Водяная стена
const int SPL_Reserved_77           = 77;
const int SPL_Reserved_78           = 78;
const int SPL_Reserved_79           = 79;

// Заклинания Майя
const int SPL_Plague                = 80;   //Чума
const int SPL_Swarm                 = 81;   //Рой
const int SPL_GreenTentacle         = 82;   //Зеленые щупальца
const int SPL_Earthquake            = 83;   //Землетрясение
const int SPL_SummonGuardian        = 84;   //Создать стража
const int SPL_Energyball            = 85;   //Шар энергии
const int SPL_SuckEnergy            = 86;   //Украсть энергию
const int SPL_Skull                 = 87;   //Крик мертвых
const int SPL_SummonZombie          = 88;   //Создать зомби
const int SPL_SummonMud             = 89;   //Вызвать Муда

// ...
const int SPL_Reserved_90           = 90;
const int SPL_Reserved_91           = 91;
const int SPL_Reserved_92           = 92;
const int SPL_Reserved_93           = 93;
const int SPL_Reserved_94           = 94;
const int SPL_Reserved_95           = 95;
const int SPL_Reserved_96           = 96;
const int SPL_Reserved_97           = 97;
const int SPL_Reserved_98           = 98;
const int SPL_Reserved_99           = 99;

const int MAX_SPELL                 = 100;   //59 (Gothic),68 (Gothic2),100 (G2Addon)

//  Массив имен классов заклинаний
const string spellFxInstanceNames[MAX_SPELL] =
{
   "PalLight",             // 0    SPL_PalLight
   "PalHeal",              // 1    SPL_PalLightHeal
   "PalHolyBolt",          // 2    SPL_PalHolyBolt
   "PalHeal",              // 3    SPL_PalMediumHeal
   "PalRepelEvil",         // 4    SPL_PalRepelEvil
   "PalHeal",              // 5    SPL_PalFullHeal
   "PalDestroyEvil",       // 6    SPL_PalDestroyEvil
   "Teleport",             // 7    SPL_PalTeleportSecret
   "Teleport",             // 8    SPL_TeleportSeaport
   "Teleport",             // 9    SPL_TeleportMonastery
   "Teleport",             // 10   SPL_TeleportFarm
   "Teleport",             // 11   SPL_TeleportXardas
   "Teleport",             // 12   SPL_TeleportPassNW
   "Teleport",             // 13   SPL_TeleportPassOW
   "Teleport",             // 14   SPL_TeleportOC
   "Teleport",             // 15   SPL_TeleportOWDemonTower
   "Teleport",             // 16   SPL_TeleportTaverne
   "Teleport",             // 17   SPL_Teleport_3
   "Light",                // 18   SPL_Light
   "Firebolt",             // 19   SPL_Firebolt
   "Icebolt",              // 20   SPL_Icebolt
   "Heal",                 // 21   SPL_LightHeal
   "SummonGoblinSkeleton", // 22   SPL_SummonGoblinSkeleton
   "InstantFireball",      // 23   SPL_InstantFireball
   "Zap",                  // 24   SPL_Zap
   "SummonWolf",           // 25   SPL_SummonWolf
   "WindFist",             // 26   SPL_WindFist
   "Sleep",                // 27   SPL_Sleep
   "Heal",                 // 28   SPL_MediumHeal
   "LightningFlash",       // 29   SPL_LightningFlash
   "ChargeFireball",       // 30   SPL_ChargeFireball
   "SummonSkeleton",       // 31   SPL_SummonSkeleton
   "Fear",                 // 32   SPL_Fear
   "Icecube",              // 33   SPL_IceCube
   "ChargeZap",            // 34   SPL_ChargeZap
   "SummonGolem",          // 53   SPL_SummonGolem
   "DestroyUndead",        // 36   SPL_DestroyUndead
   "Pyrokinesis",          // 37   SPL_Pyrokinesis
   "Firestorm",            // 38   SPL_Firestorm
   "Icewave",              // 39   SPL_IceWave
   "SummonDemon",          // 40   SPL_SummonDemon
   "Heal",                 // 41   SPL_FullHeal
   "Firerain",             // 42   SPL_Firerain
   "BreathOfDeath",        // 43   SPL_BreathOfDeath
   "MassDeath",            // 44   SPL_MassDeath
   "ArmyOfDarkness",       // 45   SPL_ArmyOfDarkness
   "Shrink",               // 46   SPL_Shrink
   "Transform",            // 47   SPL_TrfSheep
   "Transform",            // 48   SPL_TrfScavenger
   "Transform",            // 49   SPL_TrfGiantRat
   "Transform",            // 50   SPL_TrfGiantBug
   "Transform",            // 51   SPL_TrfWolf
   "Transform",            // 52   SPL_TrfWaran
   "Transform",            // 53   SPL_TrfSnapper
   "Transform",            // 54   SPL_TrfWarg
   "Transform",            // 55   SPL_TrfFireWaran
   "Transform",            // 56   SPL_TrfLurker
   "Transform",            // 57   SPL_TrfShadowbeast
   "Transform",            // 58   SPL_TrfDragonSnapper
   "Charm",                // 59   SPL_Charm
   "MasterOfDisaster",     // 60   SPL_MasterOfDisaster
   "Deathbolt",            // 61   SPL_Deathbolt
   "Deathball",            // 62   SPL_Deathball
   "Concussionbolt",       // 63   SPL_Concussionbolt
   "Light",                // 64   SPL_Reserved_64
   "Light",                // 65   SPL_Reserved_65
   "Light",                // 66   SPL_Reserved_66
   "Light",                // 67   SPL_Reserved_67
   "Light",                // 68   SPL_Reserved_68
   "Light",                // 69   SPL_Reserved_69
   "Thunderstorm",         // 70   SPL_Thunderstorm
   "Whirlwind",            // 71   SPL_Whirlwind
   "Waterfist",            // 72   SPL_WaterFist
   "IceLance",             // 73   SPL_IceLance
   "Sleep",                // 74   SPL_Inflate
   "Geyser",               // 75   SPL_Geyser
   "Firerain",             // 76   SPL_Waterwall
   "Light",                // 77   SPL_Reserved_77
   "Light",                // 78   SPL_Reserved_78
   "Light",                // 79   SPL_Reserved_79
   "Fear",                 // 80   SPL_Plague
   "Swarm",                // 81   SPL_Swarm
   "Greententacle",        // 82   SPL_GreenTentacle
   "Firerain",             // 83   SPL_Earthquake
   "SummonGuardian",       // 84   SPL_SummonGuardian
   "Energyball",           // 85   SPL_Energyball
   "SuckEnergy",           // 86   SPL_SuckEnergy
   "Skull",                // 87   SPL_Skull
   "SummonZombie",         // 88   SPL_SummonZombie
   "SummonMud",            // 89   SPL_SummonMud
   "Light",                // 90   SPL_Reserved_90
   "Light",                // 91   SPL_Reserved_91
   "Light",                // 92   SPL_Reserved_92
   "Light",                // 93   SPL_Reserved_93
   "Light",                // 94   SPL_Reserved_94
   "Light",                // 95   SPL_Reserved_95
   "Light",                // 96   SPL_Reserved_96
   "Light",                // 97   SPL_Reserved_97
   "Light",                // 98   SPL_Reserved_98
   "Light"                 // 99   SPL_Reserved_99
};

// Массив имен анимаций заклинаний
const string spellFxAniLetters[MAX_SPELL] =
{
   "SLE",                  // 0     SPL_PalLight
   "HEA",                  // 1     SPL_PalLightHeal
   "FBT",                  // 2     SPL_PalHolyBolt
   "HEA",                  // 3     SPL_PalMediumHeal
   "FBT",                  // 4     SPL_PalRepelEvil
   "HEA",                  // 5     SPL_PalFullHeal
   "FIB",                  // 6     SPL_PalDestroyEvil
   "HEA",                  // 7    SPL_PalTeleportSecret
   "HEA",                  // 8    SPL_TeleportSeaport
   "HEA",                  // 9    SPL_TeleportMonastery
   "HEA",                  // 10   SPL_TeleportFarm
   "HEA",                  // 11   SPL_TeleportXardas
   "HEA",                  // 12   SPL_TeleportPassNW
   "HEA",                  // 13   SPL_TeleportPassOW
   "HEA",                  // 14   SPL_TeleportOC
   "HEA",                  // 15   SPL_TeleportOWDemonTower
   "HEA",                  // 16   SPL_TeleportTaverne
   "HEA",                  // 17   SPL_Teleport_3
   "SLE",                  // 18   SPL_Light
   "FBT",                  // 19   SPL_Firebolt
   "FBT",                  // 20   SPL_Icebolt
   "HEA",                  // 21   SPL_LightHeal
   "SUM",                  // 22   SPL_SummonGoblinSkeleton
   "FBT",                  // 23   SPL_InstantFireball
   "FBT",                  // 24   SPL_Zap
   "SUM",                  // 25   SPL_SummonWolf
   "WND",                  // 26   SPL_WindFist
   "SLE",                  // 27   SPL_Sleep
   "HEA",                  // 28   SPL_MediumHeal
   "WND",                  // 29   SPL_LightningFlash
   "FIB",                  // 30   SPL_ChargeFireball
   "SUM",                  // 31   SPL_SummonSkeleton
   "FEA",                  // 32   SPL_Fear
   "FRZ",                  // 33   SPL_IceCube
   "FIB",                  // 34   SPL_ChargeZap
   "SUM",                  // 35   SPL_SummonGolem
   "FIB",                  // 36   SPL_DestroyUndead
   "FIB",                  // 37   SPL_Pyrokinesis
   "FIB",                  // 38   SPL_Firestorm
   "FEA",                  // 39   SPL_IceWave
   "SUM",                  // 40   SPL_SummonDemon
   "HEA",                  // 41   SPL_FullHeal
   "FEA",                  // 42   SPL_Firerain
   "FIB",                  // 43   SPL_BreathOfDeath
   "MSD",                  // 44   SPL_MassDeath
   "SUM",                  // 45   SPL_ArmyOfDarkness
   "SLE",                  // 46   SPL_Shrink
   "TRF",                  // 47   SPL_TrfSheep
   "TRF",                  // 48   SPL_TrfScavenger
   "TRF",                  // 49   SPL_TrfGiantRat
   "TRF",                  // 50   SPL_TrfGiantBug
   "TRF",                  // 51   SPL_TrfWolf
   "TRF",                  // 52   SPL_TrfWaran
   "TRF",                  // 53   SPL_TrfSnapper
   "TRF",                  // 54   SPL_TrfWarg
   "TRF",                  // 55   SPL_TrfFireWaran
   "TRF",                  // 56   SPL_TrfLurker
   "TRF",                  // 57   SPL_TrfShadowbeast
   "TRF",                  // 58   SPL_TrfDragonSnapper
   "FIB",                  // 59   SPL_Charm
   "FIB",                  // 60   SPL_MasterOfDisaster
   "FBT",                  // 61   SPL_Deathbolt
   "FBT",                  // 62   SPL_Deathball
   "FBT",                  // 63   SPL_Concussionbolt
   "XXX",                  // 64   SPL_Reserved_64
   "XXX",                  // 65   SPL_Reserved_65
   "XXX",                  // 66   SPL_Reserved_66
   "XXX",                  // 67   SPL_Reserved_67
   "XXX",                  // 68   SPL_Reserved_68
   "XXX",                  // 69   SPL_Reserved_69
   "STM",                  // 70   SPL_Thunderstorm
   "WHI",                  // 71   SPL_Whirlwind
   "WND",                  // 72   SPL_WaterFist
   "FBT",                  // 73   SPL_IceLance
   "SLE",                  // 74   SPL_Inflate
   "WND",                  // 75   SPL_Geyser
   "FEA",                  // 76   SPL_Waterwall
   "XXX",                  // 77   SPL_Reserved_77
   "XXX",                  // 78   SPL_Reserved_78
   "XXX",                  // 79   SPL_Reserved_79
   "FBT",                  // 80   SPL_Plague
   "FBT",                  // 81   SPL_Swarm
   "FRZ",                  // 82   SPL_GreenTentacle
   "FEA",                  // 83   SPL_Earthquake
   "SUM",                  // 84   SPL_SummonGuardian
   "WND",                  // 85   SPL_Energyball
   "WND",                  // 86   SPL_SuckEnergy
   "WND",                  // 87   SPL_Skull
   "SUM",                  // 88   SPL_SummonZombie
   "SUM",                  // 89   SPL_SummonMud
   "XXX",                  // 90   SPL_Reserved_90
   "XXX",                  // 91   SPL_Reserved_91
   "XXX",                  // 92   SPL_Reserved_92
   "XXX",                  // 93   SPL_Reserved_93
   "XXX",                  // 94   SPL_Reserved_94
   "XXX",                  // 95   SPL_Reserved_95
   "XXX",                  // 96   SPL_Reserved_96
   "XXX",                  // 97   SPL_Reserved_97
   "XXX",                  // 98   SPL_Reserved_98
   "XXX"                   // 99   SPL_Reserved_99
};

// Таланты
const int NPC_TALENT_UNKNOWN            = 0;    //Отсутствует
const int NPC_TALENT_1H                 = 1;    //Владение одноручным оружием
const int NPC_TALENT_2H                 = 2;    //Владение двуручным оружием
const int NPC_TALENT_BOW                = 3;    //Владение луком
const int NPC_TALENT_CROSSBOW           = 4;    //Владение арбалетом
const int NPC_TALENT_PICKLOCK           = 5;    //Умение вскрывать замки
  //const int NPC_TALENT_PICKPOCKET       = 6;    //Карманная кража (из Готики 1, не используется)
const int NPC_TALENT_MAGE               = 7;    //Маг
const int NPC_TALENT_SNEAK              = 8;    //Подкрадывание
const int NPC_TALENT_REGENERATE         = 9;    //Регенерация
const int NPC_TALENT_FIREMASTER         = 10;   //Владение огнем
const int NPC_TALENT_ACROBAT            = 11;   //Акробатика
const int NPC_TALENT_PICKPOCKET         = 12;   //Карманная кража
const int NPC_TALENT_SMITH              = 13;   //Кузнечное дело
const int NPC_TALENT_RUNES              = 14;   //Изготовление рун
const int NPC_TALENT_ALCHEMY            = 15;   //Алхимия
const int NPC_TALENT_TAKEANIMALTROPHY   = 16;   //Разделка животных
const int NPC_TALENT_FOREIGNLANGUAGE    = 17;   //Изучение иностранных языков
const int NPC_TALENT_WISPDETECTOR       = 18;   //Поиск предметов
const int NPC_TALENT_C                  = 19;   //(пусто)
const int NPC_TALENT_D                  = 20;   //(пусто)
const int NPC_TALENT_E                  = 21;   //(пусто)

const int NPC_TALENT_MAX                = 22;   //Макс. число талантов

// Массив талантов изготовления рун
var int PLAYER_TALENT_RUNES[MAX_SPELL];

// Уровни знаний иностранных языков
const int LANGUAGE_1        = 0;
const int LANGUAGE_2        = 1;
const int LANGUAGE_3        = 2;

const int MAX_LANGUAGE      = 3;

var int PLAYER_TALENT_FOREIGNLANGUAGE[MAX_LANGUAGE];

// Уровни поиска предметов
const int WISPSKILL_NF              = 0;    //Поиск оружия ближнего радиуса поражения
const int WISPSKILL_FF              = 1;    //Поиск оружия дальнего радиуса поражения
const int WISPSKILL_NONE            = 2;    //Поиск денег, ключей, предметов обихода
const int WISPSKILL_RUNE            = 3;    //Поиск рун и свитков
const int WISPSKILL_MAGIC           = 4;    //Поиск колец и амулетов
const int WISPSKILL_FOOD            = 5;    //Поиск питания и растений
const int WISPSKILL_POTIONS         = 6;    //Поиск напитков всех видов

const int MAX_WISPSKILL             = 7;    //Макс. количество уровней поиска

var int PLAYER_TALENT_WISPDETECTOR[MAX_WISPSKILL];

// Режимы поиска предметов
var int WispSearching;
const int WispSearch_Follow         = 1;    //Огонек следует за вами
const int WispSearch_ALL            = 2;    //Ищет все, что может найти
const int WispSearch_POTIONS        = 3;    //Ищет напитки
const int WispSearch_MAGIC          = 4;    //Ищет магические предметы
const int WispSearch_FOOD           = 5;    //Ищет питание
const int WispSearch_NF             = 6;    //Ищет оружия ближнего радиуса поражения
const int WispSearch_FF             = 7;    //Ищет оружия дальнего радиуса поражения
const int WispSearch_NONE           = 8;    //Ищет деньги
const int WispSearch_RUNE           = 9;    //Ищет руны

// Алхимия
const int POTION_Health_01          = 0;    //Лечебная эссенция
const int POTION_Health_02          = 1;    //Лечебный экстракт
const int POTION_Health_03          = 2;    //Лечебный эликсир
const int POTION_Mana_01            = 3;    //Эссенция маны
const int POTION_Mana_02            = 4;    //Экстракт маны
const int POTION_Mana_03            = 5;    //Эликсир маны
const int POTION_Speed              = 6;    //Напиток ускорения
const int POTION_Perm_STR           = 7;    //Эликсир силы
const int POTION_Perm_DEX           = 8;    //Эликсир ловкости
const int POTION_Perm_Mana          = 9;    //Постоянный напиток маны
const int POTION_Perm_Health        = 10;   //Постоянный лечебный напиток
const int POTION_MegaDrink          = 11;   //Эмбарла Фиргасто
const int CHARGE_Innoseye           = 12;   //Зарядить Глаз
const int POTION_Mana_04            = 13;   //Чистая мана
const int POTION_Health_04          = 14;   //Чистая жизненная энергия

const int MAX_POTION                = 15;   //Макс. количество видов алхимии

var int PLAYER_TALENT_ALCHEMY[MAX_POTION];

// Кузнечное дело
const int WEAPON_Common             = 0;    //Простой самокованный меч

const int WEAPON_1H_Special_01      = 1;    //Рудный длинный клинок
const int WEAPON_2H_Special_01      = 2;    //Рудный двуручник
const int WEAPON_1H_Special_02      = 3;    //Рудный клинок чудовища
const int WEAPON_2H_Special_02      = 4;    //Тяжелый рудный двуручник
const int WEAPON_1H_Special_03      = 5;    //Рудный боевой клинок
const int WEAPON_2H_Special_03      = 6;    //Тяжелый рудный боевой клинок
const int WEAPON_1H_Special_04      = 7;    //Рудный потрошитель драконов
const int WEAPON_2H_Special_04      = 8;    //Большой рудный потрошитель драконов

const int WEAPON_1H_Harad_01        = 9;    //Благородный меч
const int WEAPON_1H_Harad_02        = 10;   //Благородный длинный меч
const int WEAPON_1H_Harad_03        = 11;   //Рубиновый клинок
const int WEAPON_1H_Harad_04        = 12;   //Благородный меч чудовища

const int MAX_WEAPONS               = 13;   //Макс. количество видов создаваемого оружия

var int PLAYER_TALENT_SMITH[MAX_WEAPONS];

// Разделка животных
const int TROPHY_Teeth              = 0;    //Взять зубы
const int TROPHY_Claws              = 1;    //Взять когти
const int TROPHY_Fur                = 2;    //Взять шкуру
const int TROPHY_Heart              = 3;    //Взять сердце
const int TROPHY_ShadowHorn         = 4;    //Взять рог мракориса
const int TROPHY_FireTongue         = 5;    //Взять огненный язык
const int TROPHY_BFWing             = 6;    //Взять крылья кровяного шершня
const int TROPHY_BFSting            = 7;    //Взять жало кровяного шершня
const int TROPHY_Mandibles          = 8;    //Взять жвало
const int TROPHY_CrawlerPlate       = 9;    //Взять панцирь ползуна
const int TROPHY_DrgSnapperHorn     = 10;   //Взять рог остера
const int TROPHY_DragoNPCale        = 11;   //Взять чешую дракона
const int TROPHY_DragonBlood        = 12;   //Взять кровь дракона
const int TROPHY_ReptileSkin        = 13;   //Взять кожу рептилии

const int MAX_TROPHIES              = 14;   //Макс. количество трофеев

var int PLAYER_TALENT_TAKEANIMALTROPHY[MAX_TROPHIES];

//****************************************
// Шрифты для движка
//****************************************
const string TEXT_FONT_20           = "Font_old_20_white.tga";
const string TEXT_FONT_10           = "Font_old_10_white.tga";
const string TEXT_FONT_DEFAULT      = "Font_old_10_white.tga";
const string TEXT_FONT_Inventory    = "Font_old_10_white.tga";

//****************************************
// Задержка текста при выводе в режиме диалога в мс на символ
//****************************************
const float VIEW_TIME_PER_CHAR      = 550;

//****************************************
//  Области игрового мира
//****************************************
const int NEWWORLD_ZEN                  = 1;    //Хоринис с окрестностями
const int OLDWORLD_ZEN                  = 2;    //Весь Миненталь
const int DRAGONISLAND_ZEN              = 3;    //Остров Драконов
const int ADDONWORLD_ZEN                = 4;    //Яркендар

//****************************************
// Параметры камеры для показа предметов инвентаря
//****************************************
const int INVCAM_ENTF_RING_STANDARD     = 400;
const int INVCAM_ENTF_AMULETTE_STANDARD = 150;
const int INVCAM_ENTF_MISC_STANDARD     = 200;
const int INVCAM_ENTF_MISC2_STANDARD    = 250;
const int INVCAM_ENTF_MISC3_STANDARD    = 500;
const int INVCAM_ENTF_MISC4_STANDARD    = 650;
const int INVCAM_ENTF_MISC5_STANDARD    = 850;
const int INVCAM_X_RING_STANDARD        = 25;
const int INVCAM_Z_RING_STANDARD        = 45;


Тема вторая: Искусственный интеллект.

Все функции, константы и переменные искусственного интеллекта находятся в папке ..\AI


1. Константы AI.

Они описаны в файле ..\AI_Intern\AI_Constants.d
//**************************
// AIVAR - Константы для людей
//**************************

// ------ Новости и память ---------------------

const int   AIV_LastFightAgainstPlayer      = 0;   //Параметры последнего сражения с ГГ
const int       FIGHT_NONE              = 0;       //Сражения с ГГ не было
const int       FIGHT_LOST              = 1;       //Сражение проиграно (победа ГГ)
const int       FIGHT_WON               = 2;       //Сражение выиграно (победа НПС)
const int       FIGHT_CANCEL            = 3;       //Сражение прервано

const int   AIV_NpcSawPlayerCommit          = 1;   //НПС видел какой криминал совершил ГГ
const int       CRIME_NONE              = 0;       //Криминала не было
const int       CRIME_SHEEPKILLER       = 1;       //ГГ убил овцу
const int       CRIME_ATTACK            = 2;       //ГГ сражался с НПС (параметры в AIV_LastFightAgainstPlayer)
const int       CRIME_THEFT             = 3;       //ГГ занимался воровством
const int       CRIME_MURDER            = 4;       //ГГ совершил убийство

const int   AIV_NpcSawPlayerCommitDay        = 2;   //День, когда ГГ совершил криминал

// ------ Восприятие диалога -------------------
const int   AIV_NpcStartedTalk              = 3;   //Устанавливается в TRUE, если НПС с важной информацией обращается к другому НПС

// ------ Диалог -------------------------------
const int   AIV_INVINCIBLE                  = 4;   //Устанавливается в TRUE в начале диалога для обоих НПС
const int   AIV_TalkedToPlayer              = 5;   //Устанавливается в TRUE, если НПС уже разговаривал с ГГ

// ------ Карманная кража ----------------------
const int   AIV_PlayerHasPickedMyPocket     = 6;   //Устанавливается в TRUE, если ГГ залезал в карман НПС

// ------ Сражение -----------------------------
const int   AIV_LASTTARGET                  = 7;   //Ссылка на последнего атакованного НПС
const int   AIV_PursuitEnd                  = 8;   //Устанавливается в TRUE, если НПС закончил преследование

// ------ Причина нападения ------------
const int   AIV_ATTACKREASON                = 9;   //Причина нападения (по приоритетам)
const int       AR_NONE                 = 0;       //Причины нет
const int       AR_LeftPortalRoom       = 1;       //ГГ находится в помещении с ограниченным доступом
const int       AR_ClearRoom            = 2;       //ГГ находится в помещении другого НПС
const int       AR_GuardCalledToRoom    = 3;       //Вызвана охрана для защиты помещения
const int       AR_MonsterVsHuman       = 4;       //Монстр сражается с человеком, ГГ помогает человеку
const int       AR_MonsterMurderedHuman = 5;       //Монстр убил человека
const int       AR_SheepKiller          = 6;       //Овца была атакована и убита (человеком или монстром)
const int       AR_Theft                = 7;       //ГГ что-то украл
const int       AR_UseMob               = 8;       //ГГ использовал МОВ с флагом принадлежности другому НПС
const int       AR_GuardCalledToThief   = 9;       //Вызвана стража по факту воровства ГГ
const int       AR_ReactToWeapon        = 10;      //Преступник не убрал оружие после двукратного пердупреждения или пустился в бега
const int       AR_ReactToDamage        = 11;      //Преступник ранил НПС
const int       AR_GuardStopsFight      = 12;      //Охрана прекращает атаковать преступника
const int       AR_GuardCalledToKill    = 13;      //Вызвана охрана по факту убийства
const int       AR_GuildEnemy           = 14;      //НПС является врагом гильдии (человек или монстр)
const int       AR_HumanMurderedHuman   = 15;      //Человек убил человека
const int       AR_MonsterCloseToGate   = 16;      //Охрана ворот атакует враждебного монстра
const int       AR_GuardStopsIntruder   = 17;      //Охрана ворот атакует незваного гостя
const int       AR_SuddenEnemyInferno   = 18;      //Немедленная атака противника (невзирая на блокировку)
const int       AR_KILL                 = 19;      //ГГ убил НПС

// ------ Обыскивание трупов --------------------
const int   AIV_RANSACKED                   = 10;  //Устанавливается TRUE, если труп обыскивается

// ------ Смерть --------------------------------
const int   AIV_DeathInvGiven               = 11;  //Устанавливается TRUE, если труп обыскан

// ------ Охрана --------------------------------
const int   AIV_Guardpassage_Status         = 12;  //Статус предупреждений охраны
const int       GP_NONE                 = 0;       //Нет предупреждений
const int       GP_FirstWarnGiven       = 1;       //НПС получил первое предупреждение
const int       GP_SecondWarnGiven      = 2;       //НПС получил второе предупреждение
const int   AIV_LastDistToWP                = 13;  //Расстояние от WP охраны до точки, в которой НПС получил предупреждение
const int   AIV_PASSGATE                    = 14;  //TRUE - проход возможен, FALSE - проход запрещен

// ------ Предоставление экспы ------------------
const int   AIV_PARTYMEMBER                 = 15;  //Если TRUE, то экспа за убитых монстров сопровождавшим идет ГГ.
const int   AIV_VictoryXPGiven              = 16;  //Устанавливается TRUE, когда экспа за победу начислена

// ------ Пол -----------------------------------
const int AIV_Gender                        = 17;  //Пол НПС
const int       MALE                    = 0;       //Мужской
const int       FEMALE                  = 1;       //Женский

// ------ Пища ----------------------------------
const int AIV_Food                          = 18;  //Употребляемая пища
const int       FOOD_Apple              = 0;       //Яблоко
const int       FOOD_Cheese             = 1;       //Сыр
const int       FOOD_Bacon              = 2;       //Бекон
const int       FOOD_Bread              = 3;       //Хлеб

// ------ Вспомогательные параметры для ТА ------
const int AIV_TAPOSITION                    = 19;  //Позиции выполненния ТА
const int       ISINPOS                 = 0;       //НПС в позиции выполнения ТА
const int       NOTINPOS                = 1;       //НПС не в позиции выполнения ТА
const int       NOTINPOS_WALK           = 2;       //НПС идет к точке выполнения ТА

// ------ Выбор заклинания ----------------------
const int AIV_SelectSpell                   = 20;  //Случайное число, соответствующее заклинанию, выбранному НПС

// ------ Наблюдение ----------------------------
const int AIV_SeenLeftRoom                  = 21;  //TRUE, если НПС наблюдает за своим помещением

////////////////////////////////////               //Переменные использующиеся при комментариях действий ГГ
var int Player_SneakerComment;                     //ГГ крадется
var int Player_LeftRoomComment;                    //ГГ зашел в чужое помещение
var int Player_DrawWeaponComment;                  //ГГ вытащил оружие
var int Player_GetOutOfMyBedComment;               //ГГ лег на чужую кровать
////////////////////////////////////

// ------ Сражение со вторым противником --------
const int AIV_HitByOtherNpc                 = 22;  //Расстояние до другого НПС
const int AIV_WaitBeforeAttack              = 23;  //Если != 0, то вводится задержка перед атакой
const int AIV_LastAbsolutionLevel           = 24;  //Репутация ГГ для конкретной локации

// ----------------------------------------------
const int AIV_ToughGuyNewsOverride          = 25;  //НПС не имеет новостей о "жестком парне" (ГГ)

//***************************
// AIVAR - Константы для монстров
//***************************

const int AIV_MM_ThreatenBeforeAttack       = 26;  //Монстр угрожает перед атакой
const int AIV_MM_FollowTime                 = 27;  //Время преследования монстром в сек.
const int       FOLLOWTIME_SHORT        = 5;       //Короткое преследование
const int       FOLLOWTIME_MEDIUM       = 10;      //Среднее преследование
const int       FOLLOWTIME_LONG         = 20;      //Длительное преследование
const int AIV_MM_FollowInWater              = 28;  //Полезет ли монстр за НПС в воду? (относится и к людям)
// ----------------------------------------------
const int AIV_MM_PRIORITY                   = 29;  //Приоритет поведения монстра
const int   PRIO_EAT                    = 0;       //Пожирание падали
const int   PRIO_ATTACK                 = 1;       //Нападение на жертву
// ----------------------------------------------
const int AIV_MM_SleepStart                 = 30;  //Время отхода монстра ко сну (час)
const int AIV_MM_SleepEnd                   = 31;  //Время пробуждения
const int AIV_MM_RestStart                  = 32;  //Время начала отдыха
const int AIV_MM_RestEnd                    = 33;  //Время окончания отдыха
const int AIV_MM_RoamStart                  = 34;  //Время начала брожения
const int AIV_MM_RoamEnd                    = 35;  //Время окончания брожения
const int AIV_MM_EatGroundStart             = 36;  //Время начала кормежки
const int AIV_MM_EatGroundEnd               = 37;  //Время окончания кормежки
const int AIV_MM_WuselStart                 = 38;  //Время начала активности
const int AIV_MM_WuselEnd                   = 39;  //Время окончания активности
const int AIV_MM_OrcSitStart                = 40;  //Время, когда орк садится
const int AIV_MM_OrcSitEnd                  = 41;  //Время, когда орк встает
const int   OnlyRoutine                 = -1;      //Время контролируется программой поведения монстра
// ----------------------------------------------
const int AIV_MM_ShrinkState                = 42;  //Состояние уменьшения монстра (коэффициент сжатия)
// ----------------------------------------------
const int AIV_MM_REAL_ID                    = 43;  //Идентификатор монстра
const int   ID_MEATBUG                  = 1;       //Мясной жук
const int   ID_SHEEP                    = 2;       //Овца
const int   ID_GOBBO_GREEN              = 3;       //Гоблин
const int   ID_GOBBO_BLACK              = 4;       //Черный гоблин
const int   ID_GOBBO_SKELETON           = 5;       //Скелет гоблина
const int   ID_SUMMONED_GOBBO_SKELETON  = 6;       //Вызванный Скелет гоблина
const int   ID_SCAVENGER                = 7;       //Падальщик
const int   ID_SCAVENGER_DEMON          = 8;       //Зубастик
const int   ID_GIANT_RAT                = 8;       //Крыса
const int   ID_GIANT_BUG                = 9;       //Полевой жук
const int   ID_BLOODFLY                 = 10;      //Кровяной шершень
const int   ID_WARAN                    = 11;      //Ящерица
const int   ID_FIREWARAN                = 12;      //Огненная ящерица
const int   ID_WOLF                     = 13;      //Волк
const int   ID_WARG                     = 14;      //Варг
const int   ID_SUMMONED_WOLF            = 15;      //Вызванный Волк
const int   ID_MINECRAWLER              = 16;      //Ползун
const int   ID_MINECRAWLERWARRIOR       = 17;      //Воин ползунов
const int   ID_LURKER                   = 18;      //Шныг
const int   ID_SKELETON                 = 19;      //Скелет
const int   ID_SUMMONED_SKELETON        = 20;      //Вызванный Скелет
const int   ID_SKELETON_MAGE            = 21;      //Маг скелетов
const int   ID_ZOMBIE                   = 22;      //Зомби
const int   ID_SNAPPER                  = 23;      //Глорх
const int   ID_DRAGONSNAPPER            = 24;      //Остер
const int   ID_SHADOWBEAST              = 25;      //Мракорис
const int   ID_SHADOWBEAST_SKELETON     = 26;      //Скелет мракориса
const int   ID_HARPY                    = 27;      //Гарпия
const int   ID_STONEGOLEM               = 28;      //Каменный голем
const int   ID_FIREGOLEM                = 29;      //Огненный голем
const int   ID_ICEGOLEM                 = 30;      //Ледяной голем
const int   ID_SUMMONED_GOLEM           = 31;      //Вызванный Голем
const int   ID_DEMON                    = 32;      //Демон
const int   ID_SUMMONED_DEMON           = 33;      //Вызванный Демон
const int   ID_DEMON_LORD               = 34;      //Лорд демонов
const int   ID_TROLL                    = 35;      //Троль
const int   ID_TROLL_BLACK              = 36;      //Черный троль
const int   ID_SWAMPSHARK               = 37;      //Болотожор
const int   ID_DRAGON_FIRE              = 38;      //Огненный дракон
const int   ID_DRAGON_ICE               = 39;      //Ледяной дракон
const int   ID_DRAGON_ROCK              = 40;      //Каменный дракон
const int   ID_DRAGON_SWAMP             = 41;      //Болотный дракон
const int   ID_DRAGON_UNDEAD            = 42;      //Дракон нежить
const int   ID_MOLERAT                  = 43;      //Кротокрыс
const int   ID_ORCWARRIOR               = 44;      //Воин орков
const int   ID_ORCSHAMAN                = 45;      //Шаман орков
const int   ID_ORCELITE                 = 46;      //Элитный воин орков
const int   ID_UNDEADORCWARRIOR         = 47;      //Орк нежить
const int   ID_DRACONIAN                = 48;      //Ящер
const int   ID_WISP                     = 49;      //Огонек

//-----Addon ------

const int   ID_Alligator                = 50;      //Аллигатор
const int   ID_Swampgolem               = 51;      //Болотный голем
const int   ID_Stoneguardian            = 52;      //Каменный сторож
const int   ID_Gargoyle                 = 53;      //Огненный дух мракориса
const int   ID_Bloodhound               = 54;      //Кровяная собака
const int   ID_Icewolf                  = 55;      //Ледяной волк
const int   ID_OrcBiter                 = 56;      //Зубастик
const int   ID_Razor                    = 57;      //Расчленитель
const int   ID_Swarm                    = 58;      //Рой
const int   ID_Swamprat                 = 59;      //Болотная крыса
const int   ID_BLATTCRAWLER             = 60;      //Богомол
const int   ID_SummonedGuardian         = 61;      //Вызванный Каменный страж
const int   ID_SummonedZombie           = 62;      //Вызванный Зомби
const int   ID_Keiler                   = 63;      //Кабан
const int   ID_SWAMPDRONE               = 64;      //Болотная вонючка

// ----------------------------------------------
const int AIV_LASTBODY                      = 44;  //Ссылка на последнего сожранного монстра

//**********************
// Дальнейшие человеческие AIVARы
//**********************

// ------ Сражение с Альриком -------------------
const int AIV_ArenaFight                    = 45;  //Параметры сражения
const int   AF_NONE                     = 0;       //Сражения не было
const int   AF_RUNNING                  = 1;       //В процессе сражения
const int   AF_AFTER                    = 2;       //После сражения
const int   AF_AFTER_PLUS_DAMAGE        = 3;       //ГГ нечестно выиграл сражение

// ------ Криминал ------------------------------
const int AIV_CrimeAbsolutionLevel          = 46;  //Абсолютный уровень криминальной репутации ГГ

// ------ Атака ---------------------------------
const int AIV_LastPlayerAR                  = 47;  //Причина последнего нападения ГГ

// ------ Без сознания --------------------------
const int AIV_DuelLost                      = 48;  //TRUE, если НПС из ополчения был побит ГГ

// ------ Торговля ------------------------------
const int AIV_ChapterInv                    = 49;  //Отвечает за обновление товаров у торговцев в начале главы

// ------ Стая монстров -------------------------
const int AIV_MM_Packhunter                 = 50;  //TRUE, если монстры одного вида охотятся и атакуют стаей

// ------ Магия ---------------------------------
const int AIV_MagicUser                     = 51;  //Использует ли НПС магию
const int   MAGIC_NEVER                 = 0;       //Не использует
const int   MAGIC_ALWAYS                = 1;       //Всегда использует

// ------ Послушники ----------------------------
const int AIV_DropDeadAndKill               = 52;  //TRUE, если этого НПС разрешено убить

// ------ Магия заморозки -----------------------
const int AIV_FreezeStateTime               = 53;  //Время начала действия заклинаний заморозки

// ------ Игнорирование криминала ---------------
const int AIV_IGNORE_Murder                 = 54;  //Игнорирование убийства
const int AIV_IGNORE_Theft                  = 55;  //Игнорирование воровства
const int AIV_IGNORE_Sheepkiller            = 56;  //Игнорирование убийства овцы

// ------ Крутой парень -------------------------
const int AIV_ToughGuy                      = 57;  //TRUE, если НПС имеет дело с "крутым парнем"

// ------ Отвергнутые новости -------------------
const int AIV_NewsOverride                  = 58;  //TRUE, если НПС не знает никаких новостей

// ------ Атака монстров ------------------------
const int AIV_MaxDistToWp                   = 59;  //Макс. дистанция, на которой монстр начинает атаковать
const int AIV_OriginalFightTactic           = 60;  //Тактика нападения монстра

// ------ Оценка врага --------------------------
const int AIV_EnemyOverride                 = 61;  //TRUE, если врагов у НПС нет

// ------ Вызванные монстры ---------------------
const int AIV_SummonTime                    = 62;  //Время от начала вызова монстра

// ------ Атака ---------------------------------
const int AIV_FightDistCancel               = 63;  //Дистанция, на которой прекращается сражение
const int AIV_LastFightComment              = 64;  //TRUE, если НПС комментировал последнее сражение

// ----------------------------------------------
const int AIV_LOADGAME                      = 65;  //TRUE, если была загрузка игры (other не инициализирован)

// ------ Без сознания --------------------------
const int AIV_DefeatedByPlayer              = 66;  //TRUE, если НПС побежден ГГ

// ------ Смерть --------------------------------
const int AIV_KilledByPlayer                = 67;  //TRUE, если НПС убит ГГ

// ------ Различные функции ---------------------
const int AIV_StateTime                     = 68;  //Время нахождения НПС в конкретном состоянии

// ----------------------------------------------
const int AIV_Dist                          = 69;  //Дистанция между НПС
const int AIV_IgnoresFakeGuild              = 70;  //TRUE, если НПС игнорирует поддельную гильдию
const int AIV_NoFightParker                 = 71;  //TRUE, если НПС не может атаковать
const int AIV_NPCIsRanger                   = 72;  //TRUE, если НПС принадлежит к "Кольцу Воды"
const int AIV_IgnoresArmor                  = 73;  //НПС не носит броню
const int AIV_StoryBandit                   = 74;  //Бандиты, с которыми можно сражаться
const int AIV_StoryBandit_Esteban           = 75;  //НПС является охранником Эстебана

// ------ Смерч ---------------------------------
const int AIV_WhirlwindStateTime            = 76;  //Время начала запуска смерча

// ------ Вздутие -------------------------------
const int AIV_InflateStateTime              = 77;  //Время начала запуска вздутия

// ------ Рой -----------------------------------
const int AIV_SwarmStateTime                = 78;  //Время начала запуска роя

// ------ Опустошение ---------------------------
const int AIV_SuckEnergyStateTime           = 79;  //Время начала запуска опустошения

// ------ Пираты --------------------------------
const int AIV_FollowDist                    = 80;  //Дистанция следования НПС друг за другом

// ------ Реальные атрибуты НПС -----------------
const int REAL_STRENGTH                     = 81;  //Сила
const int REAL_DEXTERITY                    = 82;  //Ловкость
const int REAL_MANA_MAX                     = 83;  //Мана
const int REAL_TALENT_1H                    = 84;  //Владение одноручником
const int REAL_TALENT_2H                    = 85;  //Владение двуручником
const int REAL_TALENT_BOW                   = 86;  //Владение луком
const int REAL_TALENT_CROSSBOW              = 87;  //Владение арбалетом

const int AIV_SpellLevel                    = 88;  //Круг магии

//***************************************************
// Глобальные переменные для системы Доносов/Абсолютных уровней/Новостей
//***************************************************

// Абсолютные уровни
var int ABSOLUTIONLEVEL_OldCamp;                   //Миненталь
var int ABSOLUTIONLEVEL_City;                      //Хоринис
var int ABSOLUTIONLEVEL_Monastery;                 //Монастырь
var int ABSOLUTIONLEVEL_Farm;                      //Поместье Онара
var int ABSOLUTIONLEVEL_BL;                        //Лагерь бандитов

// Кол-во криминала в Минентале
var int PETZCOUNTER_OldCamp_Murder;                //Убийства
var int PETZCOUNTER_OldCamp_Theft;                 //Воровство
var int PETZCOUNTER_OldCamp_Attack;                //Нападения
var int PETZCOUNTER_OldCamp_Sheepkiller;           //Убийство овец

// Кол-во криминала в Хоринисе
var int PETZCOUNTER_City_Murder;
var int PETZCOUNTER_City_Theft;
var int PETZCOUNTER_City_Attack;
var int PETZCOUNTER_City_Sheepkiller;

// Кол-во криминала в Монастыре
var int PETZCOUNTER_Monastery_Murder;
var int PETZCOUNTER_Monastery_Theft;
var int PETZCOUNTER_Monastery_Attack;
var int PETZCOUNTER_Monastery_Sheepkiller;

// Кол-во криминала в Поместье Онара
var int PETZCOUNTER_Farm_Murder;
var int PETZCOUNTER_Farm_Theft;
var int PETZCOUNTER_Farm_Attack;
var int PETZCOUNTER_Farm_Sheepkiller;

// Кол-во криминала в Лагере бандитов
var int PETZCOUNTER_BL_Murder;
var int PETZCOUNTER_BL_Theft;
var int PETZCOUNTER_BL_Attack;

//*******************************
// Константы локаций
//*******************************

const int LOC_NONE      = 0;                   //Вне локаций
const int LOC_OLDCAMP   = 1;                   //Миненталь
const int LOC_CITY      = 2;                   //Хоринис
const int LOC_MONASTERY = 3;                   //Монастырь
const int LOC_FARM      = 4;                   //Поместье Онара
const int LOC_BL        = 5;                   //Лагерь бандитов
const int LOC_ALL       = 6;                   //Все локации

//*******************************
// Константы Хориниса (кварталы)
//*******************************

const int Q_KASERNE     = 1;                   //Казарма
const int Q_GALGEN      = 2;                   //Виселица
const int Q_MARKT       = 3;                   //Рынок
const int Q_TEMPEL      = 4;                   //Замок
const int Q_UNTERSTADT  = 5;                   //Нижний город
const int Q_HAFEN       = 6;                   //Гавань
const int Q_OBERSTADT   = 7;                   //Верхний город

//******************************
// Активное восприятие монстров
//******************************
//---------------------------------------------
const int PERC_DIST_SUMMONED_ACTIVE_MAX = 2000; //Макс. дальность действия активного восприятия вызываемых монстров
const int PERC_DIST_MONSTER_ACTIVE_MAX  = 1500; //Макс. дальность действия активного восприятия других монстров
const int PERC_DIST_ORC_ACTIVE_MAX      = 2500; //Макс. дальность действия активного восприятия орков
const int PERC_DIST_DRAGON_ACTIVE_MAX   = 3500; //Макс. дальность действия активного восприятия драконов
//---------------------------------------------
const int FIGHT_DIST_MONSTER_ATTACKRANGE = 700; //Дистанция, с которой монстры начинают атаковать
const int FIGHT_DIST_MONSTER_FLEE        = 300; //Дистанция, с которой монстр начинает преследование
const int FIGHT_DIST_DRAGON_MAGIC        = 700; //Дистанция применения драконами магии
//---------------------------------------------
const int MONSTER_THREATEN_TIME         = 4;   //Кол-во секунд, которые монстр угрожает перед атакой
const int MONSTER_SUMMON_TIME           = 60;  //Время, на которое вызываются монстры

//************************************
// Константы дистанций для человека
//************************************
// --------------------------------------------
const int TA_DIST_SELFWP_MAX            = 500; //Дистанция до объекта, на которой начинают выполняться функции распорядка дня
// --------------------------------------------
const int PERC_DIST_ACTIVE_MAX          = 2000; //Макс. дальность действия активного восприятия
//---------------------------------------------
const int PERC_DIST_INTERMEDIAT         = 1000; //Макс. дальность действия пассивного восприятия
const int PERC_DIST_DIALOG              = 500;  //Дистанция начала диалога
const int PERC_DIST_HEIGHT              = 1000; //С какой разницы высот НПС не реагируют друг на друга
const int PERC_DIST_INDOOR_HEIGHT       = 250;  //Дистанция игнорирования через закрытые двери
//---------------------------------------------
const int FIGHT_DIST_MELEE              = 600;  //Дистанция атаки оружием ближнего радиуса действия
const int FIGHT_DIST_RANGED_INNER       = 900;  //Дистанция, ближе которой НПС выбирает оружие ближнего радиуса поражения
const int FIGHT_DIST_RANGED_OUTER       = 1000; //Дистанция, дальше которой НПС выбирает оружие дальнего радиуса поражения
const int FIGHT_DIST_CANCEL             = 3500; //На этой дистанции НПС перестают преследовать
//---------------------------------------------
const int WATCHFIGHT_DIST_MIN           = 300 ; //Мин. дистанция наблюдения сражения между двумя НПС другим НПС (не милицией)
const int WATCHFIGHT_DIST_MAX           = 2000; //Мах. дистанция наблюдения сражения между двумя НПС другим НПС (не милицией)
//---------------------------------------------
const int ZivilAnquatschDist            = 400;  //Дистанция, с которой к ГГ начинают обращаться граждане
//---------------------------------------------
const float RANGED_CHANCE_MINDIST       = 1500; // (не используется)
const float RANGED_CHANCE_MAXDIST       = 4500; // (не используется)

//*************************************
// Константы времени
//*************************************
const int NPC_ANGRY_TIME                = 120; // (не используется)
// -------------------------------------------
const int HAI_TIME_UNCONPCIOUS          = 20;  //Время нахождения в бессознательном состоянии
// -------------------------------------------
const int NPC_TIME_FOLLOW               = 10;  //Время преследования монстрами

//*************************************
// Минимальные повреждения
//*************************************
const int NPC_MINIMAL_DAMAGE    = 5;           // (не используется)
const int NPC_MINIMAL_PERCENT   = 10;          // (не используется)

//*************************************
// AI константы сражения
//*************************************

//Тактика сражения
const int FAI_HUMAN_COWARD              = 2;   //Трус
const int FAI_HUMAN_NORMAL              = 42;  //Нормальный
const int FAI_HUMAN_STRONG              = 3;   //Сильный
const int FAI_HUMAN_MASTER              = 4;   //Мастер
//-----------------------------------------------
const int FAI_MONSTER_COWARD            = 10;  //Монстр трус
//-----------------------------------------------
const int FAI_NAILED                    = 1;   //Тактика сражения охранников крепостных стен
//-----------------------------------------------
// Тактика атаки монстрами
const int FAI_GOBBO                     = 7;   //Гоблины
const int FAI_SCAVENGER                 = 15;  //Падальщики
const int FAI_GIANT_RAT                 = 11;  //Крыса
const int FAI_GIANT_BUG                 = 31;  //Полевой жук
const int FAI_BLOODFLY                  = 24;  //Кровяной шершень
const int FAI_WARAN                     = 21;  //Ящерицы
const int FAI_WOLF                      = 22;  //Волк
const int FAI_MINECRAWLER               = 5;   //Ползуны
const int FAI_LURKER                    = 9;   //Шныг
const int FAI_ZOMBIE                    = 23;  //Зомби
const int FAI_SNAPPER                   = 18;  //Глорх
const int FAI_SHADOWBEAST               = 16;  //Мракорис
const int FAI_HARPY                     = 36;  //Гарпия
const int FAI_STONEGOLEM                = 8;   //Големы
const int FAI_DEMON                     = 6;   //Демоны
const int FAI_TROLL                     = 20;  //Троль
const int FAI_SWAMPSHARK                = 19;  //Болотожор
const int FAI_DRAGON                    = 39;  //Драконы
const int FAI_MOLERAT                   = 40;  //Кротокрыс
//-----------------------------------------------
const int FAI_ORC                       = 12;  //Орки
const int FAI_DRACONIAN                 = 41;  //Ящер

const int FAI_Alligator                 = 43;  //Аллигатор
const int FAI_Gargoyle                  = 44;  //Огненный дух мракориса
const int FAI_Bear                      = 45;  //Медведь
const int FAI_Stoneguardian             = 46;  //Каменный сторож

//*************************************
// Общие константы
//*************************************
const int TRUE              = 1;               //Значение TRUE
const int FALSE             = 0;               //Значение FALSE

const int LOOP_CONTINUE     = 0;               //Значение продолжения цикла
const int LOOP_END          = 1;               //Значение выхода из цикла

const int DEFAULT           = 0;               //Значение по умолчанию

//*************************************
// Константы ГГ
//*************************************

const int LP_PER_LEVEL              = 10;      //Кол-во пунктов обучения за уровень
const int HP_PER_LEVEL              = 12;      //Кол-во пунктов жизни за уровень

const int XP_PER_VICTORY            = 10;      //Ко-во экспы за уровень побежденного противника

//*************************************
// Типы НПС
//*************************************

const int NPCTYPE_AMBIENT       = 0;           //Окружающий народ
const int NPCTYPE_MAIN          = 1;           //Значимые НПС
const int NPCTYPE_FRIEND        = 2;           //Друзья
const int NPCTYPE_OCAMBIENT     = 3;           //Окружающий народ в Минентале
const int NPCTYPE_OCMAIN        = 4;           //Значимые НПС в Минентале
const int NPCTYPE_BL_AMBIENT    = 5;           //Окружающий народ в Лагере бандитов
const int NPCTYPE_TAL_AMBIENT   = 6;           //Окружающий народ в Долине бандитов
const int NPCTYPE_BL_MAIN       = 7;           //Значимые НПС в Лагере бандитов

//**************************************
//  MOBsi константы
//**************************************

const int   MOBSI_NONE                  = 0;   //Нет
const int   MOBSI_SmithWeapon           = 1;   //Ковать оружие
const int   MOBSI_SleepAbit             = 2;   //Способность спать
const int   MOBSI_MakeRune              = 3;   //Изготовление рун
const int   MOBSI_PotionAlchemy         = 4;   //Алхимия
const int   MOBSI_PRAYSHRINE            = 5;   //Молитва
const int   MOBSI_GOLDHACKEN            = 6;   //Милостыня
const int   MOBSI_PRAYIDOL              = 7;   //Молитва идолу

var int     PLAYER_MOBSI_PRODUCTION;           //Переменная для значений MOBsi

//**************************************
// Константы текстур
//**************************************

// Для мужчин и женщин (по 4 штуки)
const int BodyTex_P                     = 0;   //Бледнокожий
const int BodyTex_N                     = 1;   //Европеец
const int BodyTex_L                     = 2;   //Латиноамериканец
const int BodyTex_B                     = 3;   //Негр
const int BodyTexBabe_P                 = 4;   //Бледнокожая
const int BodyTexBabe_N                 = 5;   //Европейка
const int BodyTexBabe_L                 = 6;   //Латиноамериканка
const int BodyTexBabe_B                 = 7;   //Негритянка
const int BodyTex_Player                = 8;   //ГГ

const int BodyTex_T                     = 10;  //Татуированный мужик
const int BodyTexBabe_F                 = 11;  //Татуированная женщина
const int BodyTexBabe_S                 = 12;  //Чернокожий ребенок

const int NO_ARMOR                      = -1;  //Без доспехов

// Лица мужчин
const int Face_N_Gomez              =   0;
const int Face_N_Scar               =   1;
const int Face_N_Raven              =   2;
const int Face_N_Bullit             =   3;
const int Face_B_Thorus             =   4;
const int Face_N_Corristo           =   5;
const int Face_N_Milten             =   6;
const int Face_N_Bloodwyn           =   7;
const int Face_L_Scatty             =   8;
const int Face_N_YBerion            =   9;
const int Face_N_CoolPock           =   10;
const int Face_B_CorAngar           =   11;
const int Face_B_Saturas            =   12;
const int Face_N_Xardas             =   13;
const int Face_N_Lares              =   14;
const int Face_L_Ratford            =   15;
const int Face_N_Drax               =   16;
const int Face_B_Gorn               =   17;
const int Face_N_Player             =   18;
const int Face_P_Lester             =   19;
const int Face_N_Lee                =   20;
const int Face_N_Torlof             =   21;
const int Face_N_Mud                =   22;
const int Face_N_Ricelord           =   23;
const int Face_N_Horatio            =   24;
const int Face_N_Richter            =   25;
const int Face_N_Cipher_neu         =   26;
const int Face_N_Homer              =   27;
const int Face_B_Cavalorn           =   28;
const int Face_L_Ian                =   29;
const int Face_L_Diego              =   30;
const int Face_N_MadPsi             =   31;
const int Face_N_Bartholo           =   32;
const int Face_N_Snaf               =   33;
const int Face_N_Mordrag            =   34;
const int Face_N_Lefty              =   35;
const int Face_N_Wolf               =   36;
const int Face_N_Fingers            =   37;
const int Face_N_Whistler           =   38;
const int Face_P_Gilbert            =   39;
const int Face_L_Jackal             =   40;

// Бледнолицые
const int Face_P_ToughBald          =   41;
const int Face_P_Tough_Drago        =   42;
const int Face_P_Tough_Torrez       =   43;
const int Face_P_Tough_Rodriguez    =   44;
const int Face_P_ToughBald_Nek      =   45;
const int Face_P_NormalBald         =   46;
const int Face_P_Normal01           =   47;
const int Face_P_Normal02           =   48;
const int Face_P_Normal_Fletcher    =   49;
const int Face_P_Normal03           =   50;
const int Face_P_NormalBart01       =   51;
const int Face_P_NormalBart_Cronos  =   52;
const int Face_P_NormalBart_Nefarius =   53;
const int Face_P_NormalBart_Riordian =   54;
const int Face_P_OldMan_Gravo       =   55;
const int Face_P_Weak_Cutter        =   56;
const int Face_P_Weak_Ulf_Wohlers   =   57;

// Европейцы
const int Face_N_Important_Arto     =   58;
const int Face_N_ImportantGrey      =   59;
const int Face_N_ImportantOld       =   60;
const int Face_N_Tough_Lee_ähnlich  =   61;
const int Face_N_Tough_Skip         =   62;
const int Face_N_ToughBart01        =   63;
const int Face_N_Tough_Okyl         =   64;
const int Face_N_Normal01           =   65;
const int Face_N_Normal_Cord        =   66;
const int Face_N_Normal_Olli_Kahn   =   67;
const int Face_N_Normal02           =   68;
const int Face_N_Normal_Spassvogel  =   69;
const int Face_N_Normal03           =   70;
const int Face_N_Normal04           =   71;
const int Face_N_Normal05           =   72;
const int Face_N_Normal_Stone       =   73;
const int Face_N_Normal06           =   74;
const int Face_N_Normal_Erpresser   =   75;
const int Face_N_Normal07           =   76;
const int Face_N_Normal_Blade       =   77;
const int Face_N_Normal08           =   78;
const int Face_N_Normal14           =   79;
const int Face_N_Normal_Sly         =   80;
const int Face_N_Normal16           =   81;
const int Face_N_Normal17           =   82;
const int Face_N_Normal18           =   83;
const int Face_N_Normal19           =   84;
const int Face_N_Normal20           =   85;
const int Face_N_NormalBart01       =   86;
const int Face_N_NormalBart02       =   87;
const int Face_N_NormalBart03       =   88;
const int Face_N_NormalBart04       =   89;
const int Face_N_NormalBart05       =   90;
const int Face_N_NormalBart06       =   91;
const int Face_N_NormalBart_Senyan  =   92;
const int Face_N_NormalBart08       =   93;
const int Face_N_NormalBart09       =   94;
const int Face_N_NormalBart10       =   95;
const int Face_N_NormalBart11       =   96;
const int Face_N_NormalBart12       =   97;
const int Face_N_NormalBart_Dexter  =   98;
const int Face_N_NormalBart_Graham  =   99;
const int Face_N_NormalBart_Dusty   =   100;
const int Face_N_NormalBart16       =   101;
const int Face_N_NormalBart17       =   102;
const int Face_N_NormalBart_Huno    =   103;
const int Face_N_NormalBart_Grim    =   104;
const int Face_N_NormalBart20       =   105;
const int Face_N_NormalBart21       =   106;
const int Face_N_NormalBart22       =   107;
const int Face_N_OldBald_Jeremiah   =   108;
const int Face_N_Weak_Ulbert        =   109;
const int Face_N_Weak_BaalNetbek    =   110;
const int Face_N_Weak_Herek         =   111;
const int Face_N_Weak04             =   112;
const int Face_N_Weak05             =   113;
const int Face_N_Weak_Orry          =   114;
const int Face_N_Weak_Asghan        =   115;
const int Face_N_Weak_Markus_Kark   =   116;
const int Face_N_Weak_Cipher_alt    =   117;
const int Face_N_NormalBart_Swiney  =   118;
const int Face_N_Weak12             =   119;

// Латиноамериканцы
const int Face_L_ToughBald01        =   120;
const int Face_L_Tough01            =   121;
const int Face_L_Tough02            =   122;
const int Face_L_Tough_Santino      =   123;
const int Face_L_ToughBart_Quentin  =   124;
const int Face_L_Normal_GorNaBar    =   125;
const int Face_L_NormalBart01       =   126;
const int Face_L_NormalBart02       =   127;
const int Face_L_NormalBart_Rufus   =   128;

// Негры
const int Face_B_ToughBald          =   129;
const int Face_B_Tough_Pacho        =   130;
const int Face_B_Tough_Silas        =   131;
const int Face_B_Normal01           =   132;
const int Face_B_Normal_Kirgo       =   133;
const int Face_B_Normal_Sharky      =   134;
const int Face_B_Normal_Orik        =   135;
const int Face_B_Normal_Kharim      =   136;

// Лица женщин
const int FaceBabe_N_BlackHair      =   137;
const int FaceBabe_N_Blondie        =   138;
const int FaceBabe_N_BlondTattoo    =   139;
const int FaceBabe_N_PinkHair       =   140;
const int FaceBabe_L_Charlotte      =   141;
const int FaceBabe_B_RedLocks       =   142;
const int FaceBabe_N_HairAndCloth   =   143;
const int FaceBabe_N_WhiteCloth     =   144;
const int FaceBabe_N_GreyCloth      =   145;
const int FaceBabe_N_Brown          =   146;
const int FaceBabe_N_VlkBlonde      =   147;
const int FaceBabe_N_BauBlonde      =   148;
const int FaceBabe_N_YoungBlonde    =   149;
const int FaceBabe_N_OldBlonde      =   150;
const int FaceBabe_P_MidBlonde      =   151;
const int FaceBabe_N_MidBauBlonde   =   152;
const int FaceBabe_N_OldBrown       =   153;
const int FaceBabe_N_Lilo           =   154;
const int FaceBabe_N_Hure           =   155;
const int FaceBabe_N_Anne           =   156;
const int FaceBabe_B_RedLocks2      =   157;
const int FaceBabe_L_Charlotte2     =   158;

// Лицо Фортуно
const int Face_N_Fortuno            =   159;

// Лица пиратов
const int Face_P_Greg               =   160;
const int Face_N_Pirat01            =   161;
const int Face_N_ZombieMud          =   162;


2. Состояния тела НПС, фокусы камеры и параметры гильдий.

Все состояния тела НПС описаны в файле ..\AI_Intern\BodyStates.d, правда многие из них в игре не используются.
//*************************************************
// Состояния тела
//*************************************************

// Флаги прерываний
const int BS_FLAG_INTERRUPTABLE     =   32768;  //Состояние прерываемо
const int BS_FLAG_FREEHANDS         =   65536;  //Свободные руки

//******************************************
// Флаги состояние тела
//******************************************
const int BS_STAND                  =   0 | BS_FLAG_INTERRUPTABLE | BS_FLAG_FREEHANDS;  //Стоит
const int BS_WALK                   =   1 | BS_FLAG_INTERRUPTABLE;      //Идет
const int BS_SNEAK                  =   2 | BS_FLAG_INTERRUPTABLE;      //Подкрадывается
const int BS_RUN                    =   3;                              //Бежит
const int BS_SPRINT                 =   4;                              //Быстро бежит
const int BS_SWIM                   =   5;                              //Плывет
const int BS_CRAWL                  =   6;                              //Ползет
const int BS_DIVE                   =   7;                              //Ныряет
const int BS_JUMP                   =   8;                              //Прыгает
const int BS_CLIMB                  =   9 | BS_FLAG_INTERRUPTABLE;      //Взбирается
const int BS_FALL                   =   10;                             //Падает
const int BS_SIT                    =   11 | BS_FLAG_FREEHANDS;         //Сидит
const int BS_LIE                    =   12;                             //Лежит
const int BS_INVENTORY              =   13;                             //Открывает инвентарь
const int BS_ITEMINTERACT           =   14 | BS_FLAG_INTERRUPTABLE;     //Действие с предметом
const int BS_MOBINTERACT            =   15;                             //Действие с MOBом
const int BS_MOBINTERACT_INTERRUPT  =   16 | BS_FLAG_INTERRUPTABLE;     //Прерываемое действие с MOBом

const int BS_TAKEITEM               =   17;                             //Берет предмет
const int BS_DROPITEM               =   18;                             //Выкидывает предмет
const int BS_THROWITEM              =   19;                             //Бросает предмет
const int BS_PICKPOCKET             =   20 | BS_FLAG_INTERRUPTABLE;     //Карманная кража

const int BS_STUMBLE                =   21;                             //(не используется)
const int BS_UNCONPCIOUS            =   22;                             //Без сознания
const int BS_DEAD                   =   23;                             //Мертвый

const int BS_AIMNEAR                =   24;                             //Близкая цель
const int BS_AIMFAR                 =   25;                             //Далекая цель
const int BS_HIT                    =   26 | BS_FLAG_INTERRUPTABLE;     //Удар
const int BS_PARADE                 =   27;                             //(не используется)

// Магия
const int BS_CASTING                =   28;                             //Кастует
const int BS_PETRIFIED              =   29;                             //Ошеломленный
const int BS_CONTROLLING            =   30 | BS_FLAG_INTERRUPTABLE;     //Управляемый

const int BS_MAX                    =   31;                             //Макс. кол-во состояний тела

// Модификаторы состояний
const int BS_MOD_HIDDEN             =   128;                            //Скрытый
const int BS_MOD_DRUNK              =   256;                            //Пьяный
const int BS_MOD_NUTS               =   512;                            //???
const int BS_MOD_BURNING            =   1024;                           //Горящий
const int BS_MOD_CONTROLLED         =   2048;                           //Управляемый
const int BS_MOD_TRANSFORMED        =   4096;                           //Трансформируемый

// имена MOBов
const string MOB_SIT                =   "BENCH,CHAIR,GROUND,THRONE";
const string MOB_LIE                =   "BED,BEDHIGH,BEDLOW";
const string MOB_CLIMB              =   "CLIMB,LADDER,RANKE";
const string MOB_NOTINTERRUPTABLE   =   "DOOR,LEVER,TOUCHPLATE,TURNSWITCH,VWHEEL,CHESTBIG,CHESTSMALL,HERB,IDOL,PAN,SMOKE,INNOS";
// Все другие MOBы, здесь не приведенные, являются прерываемыми, это:
// BOOK,BSANVIL,BSCOOL,BSFIRE,BSSHARP,CAULDRON,DRUM,GRAVE,LAB,ORE,REPAIR,RMAKER,BAU,MSAEGE,STOVE,SDW(Stein des Wissens)
В файле ..\AI_Intern\Focus.d описываются фокусы камеры (производные instance от класса c_focus) для следующих ситуаций:
Focus_Normal - обычный фокус камеры
Focus_Melee - фокус камеры при сражении оружием ближнег радиуса поражения
Focus_Ranged - фокус камеры при сражении оружием дальнего радиуса поражения
Focus_Throw_Item - фокус камеры при броске (метании) предмета
Focus_Throw_Mob - фокус камеры при броске (метании) МОВа
Focus_Magic - фокус камеры при кастовании заклинания

В файле ..\AI_Intern\Species.d описывается значение параметров всех гильдий
(производная instance gil_values от класса c_gilvalues)


3. Вывод отладочной информации.

Функции, ответственные за вывод отладочной информации, находятся в файле ..\AI_Intern\PrintDebug.d
К сожалению, все эти функции выводят информацию в окно zSpy с идентификатором определенного канала. На мой взгляд их применение очень проблематично и пользы от него мало. В скриптах по понятной причине вызов этих функций отсутствует, поэтому мы их рассматривать не будем.
В файле ..\AI_Intern\PrintPlus.d приведена функция вывода на экран строки текста со случайным числом в диапазоне от 0 до 100 в следующем формате: <text><random value 0-100>. Хоть эта функция и вызывается из одного файла, смысла ее применения я не вижу.
Эти файлы можно вообще исключить из компиляции.

Есть еще один файл ..\AI_Intern\NPC_SetToMad.d, правда он к отладке отношения не имеет, но заслуживает удаления по причине его бесполезности.

P.S. В скриптах очень много неиспользуемой и ненужной информации, для убыстрения работы игры и минимизации памяти все неиспользуемые функции и переменные можно удалить.
ВНИМАНИЕ!!! Нельзя удалять неиспользуемые поля в классах, а также добавлять новые, это потребует переделки экзешника!


4. Инициализация восприятий.

Функции инициализации восприятий описаны в файле ..\AI_Intern\Perception.d
//**************************************************
// Устанавливается дальность действия восприятий НПС
//**************************************************

func void InitPerceptions()
{
   // Дальность действия всех активных восприятий определена константой PERC_DIST_ACTIVE_MAX, которая записывается в поле senses_range производной от класса c_npc.
   // PERC_ASSESSPLAYER - восприятие ГГ другими НПС
   // PERC_ASSESSENEMY - восприятие одного НПС другим НПС, у которого установлен флаг ATT_HOSTILE (враг), если Temp_Att != GilAtt, то возмется Temp_Att.
   // PERC_ASSESSBODY - восприятие НПС мертвого тела, в настоящее время используется только в Monster-AI функциях.
   // PERC_ASSESSFIGHTER - восприятие НПС ГГ с поднятым оружием или восприятие НПС всех монстров (они всегда находятся в режиме Fightmode)
   // PERC_ASSESSITEM - восприятие НПС предметов

   //*** ВНИМАНИЕ *** Активное восприятие работает только в случае, если у НПС активированы одно или несколько чувств. Зона действия активных
 восприятий 180° перед НПС по радиусу PERC_DIST_ACTIVE_MAX.

   //*** ВНИМАНИЕ *** Зона действия пассивных восприятий 360° вокруг НПС на заданное расстояние.

   // Сражение
   Perc_SetRange(PERC_ASSESSDAMAGE,9999);                      //Посылается НПС, получившему повреждение. Дальность действия не используется.
   Perc_SetRange(PERC_ASSESSOTHERSDAMAGE,PERC_DIST_INTERMEDIAT);   //Посылается всем другим (other) НПС, если НПС получил повреждение
   Perc_SetRange(PERC_ASSESSDEFEAT,WATCHFIGHT_DIST_MAX);       //Посылается всем другим НПС, если НПС находится в бессознательном состоянии
   Perc_SetRange(PERC_ASSESSMURDER,PERC_DIST_ACTIVE_MAX);      //Посылается всем другим НПС, если НПС умер
   Perc_SetRange(PERC_ASSESSTHREAT,PERC_DIST_INTERMEDIAT);     //Посылается НПС в течении 2 секунд, если в него целится ГГ
   Perc_SetRange(PERC_DRAWWEAPON,PERC_DIST_DIALOG);            //Посылается всем другим НПС, если ГГ достает оружие

   // Слух
   Perc_SetRange(PERC_ASSESSFIGHTSOUND,3000);                  //Посылается всем другим НПС, если НПС на кого-то нападает с оружием
   Perc_SetRange(PERC_ASSESSQUIETSOUND,PERC_DIST_INTERMEDIAT); //Посылается раз в секунду всем другим НПС, если ГГ идет или бежит, а также когда
 выброшен (уронен) какой-либо предмет

   // Воровство
   Perc_SetRange(PERC_ASSESSTHEFT,PERC_DIST_INTERMEDIAT);      //Посылается всем другим НПС, если ГГ украл какой-либо предмет. При карманной
 краже обворованному не посылается. При телекинезе посылается всем.
   Perc_SetRange(PERC_ASSESSUSEMOB,PERC_DIST_INTERMEDIAT);     //Посылается всем другим НПС, если ГГ использует MOBsi
   Perc_SetRange(PERC_ASSESSENTERROOM,PERC_DIST_INTERMEDIAT);  //Посылается всем другим НПС, если ГГ входит в помещение

   // Магия
   Perc_SetRange(PERC_ASSESSMAGIC,9999);                       //Посылается НПС, против которого применена магия
   Perc_SetRange(PERC_ASSESSSTOPMAGIC,9999);                   //Посылается НПС (жертве), если магия прекращает свое действие

   // Разговор
   Perc_SetRange(PERC_ASSESSTALK,PERC_DIST_DIALOG);            //Посылается НПС, если у ГГ есть важная информация для разговора

   // Взаимодействие НПС
   Perc_SetRange(PERC_ASSESSWARN,PERC_DIST_INTERMEDIAT);       //Чисто скриптовое восприятие, кому посылаем, тот и реагирует

   // Открытие дверей
   Perc_SetRange(PERC_MOVEMOB,PERC_DIST_DIALOG);               //Посылается всем другим НПС, если ГГ открывает дверь (сундук)

   // Превращение
   Perc_SetRange(PERC_ASSESSSURPRISE,FIGHT_DIST_CANCEL);       //Посылается всем другим НПС, если ГГ превращается в себя из животного

   // Не используются!
   Perc_SetRange(PERC_OBSERVEINTRUDER,100);                    //Посылается всем другим НПС, если ГГ останавливается (кроме режима подкрадывания)
   Perc_SetRange(PERC_ASSESSREMOVEWEAPON,100);                 //Посылается всем другим НПС, если ГГ прячет оружие
   Perc_SetRange(PERC_CATCHTHIEF,100);                         //Посылается всем другим НПС, если ГГ пойман на карманной краже
   Perc_SetRange(PERC_ASSESSCALL,100);                         //Посылается НПС, если ГГ призывает его к разговору (дальняя дистанция)
   Perc_SetRange(PERC_MOVENPC,100);                            //Посылается всем другим НПС, если ГГ идет вместе с НПС
   Perc_SetRange(PERC_ASSESSCASTER,100);                       //Посылается всем другим НПС, если ГГ инвестирует ману
   Perc_SetRange(PERC_NPCCOMMAND,100);                         //Чисто скриптовое восприятие, можно использовать по своему усмотрению
   Perc_SetRange(PERC_OBSERVESUSPECT,100);                     //Посылается всем другим НПС, если ГГ подкрадывается (в движении)

   //Мной не обнаружены упоминания о следующих восприятиях: PERC_ASSESSGIVENITEM, PERC_ASSESSFAKEGUILD
};

//*************************************************************
// Установка функций реакций НПС на нормальный набор восприятий для людей
//*************************************************************

func void Perception_Set_Normal()
{
   // Активные чувства при каждом состоянии активизируются по-новому, т.к. они изменяются в функции ZS_Guide_Player
   self.senses         =   SENSE_HEAR | SENSE_SEE;
   self.senses_range   =   PERC_DIST_ACTIVE_MAX;

   // Частота проверки активных восприятий
   if(Npc_KnowsInfo(self,1) || C_NpcIsGateGuard(self))     //если НПС имеет важную информацию или НПС охраняет вход
   {
       Npc_SetPercTime(self,0.3);                          //восприятия проверяются раз в 0.3 секунды
   }
   else
   {
       Npc_SetPercTime(self,1);                            //иначе восприятия проверяются раз в секунду
   };

   // Активные восприятия (приоритет наивысший)
   Npc_PercEnable(self,PERC_ASSESSPLAYER,      B_AssessPlayer);
   Npc_PercEnable(self,PERC_ASSESSENEMY,       B_AssessEnemy);

   // Пассивные восприятия (приоритет задается последовательностью регистрации)
   Npc_PercEnable(self,PERC_ASSESSMAGIC,       B_AssessMagic);
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,      B_AssessDamage);
   Npc_PercEnable(self,PERC_ASSESSMURDER,      B_AssessMurder);
   Npc_PercEnable(self,PERC_ASSESSTHEFT,       B_AssessTheft);
   Npc_PercEnable(self,PERC_ASSESSUSEMOB,      B_AssessUseMob);
   Npc_PercEnable(self,PERC_ASSESSENTERROOM,   B_AssessPortalCollision);
   Npc_PercEnable(self,PERC_ASSESSTHREAT,      B_AssessThreat);
   Npc_PercEnable(self,PERC_DRAWWEAPON,        B_AssessDrawWeapon);
   Npc_PercEnable(self,PERC_ASSESSFIGHTSOUND,  B_AssessFightSound);
   Npc_PercEnable(self,PERC_ASSESSQUIETSOUND,  B_AssessQuietSound);
   Npc_PercEnable(self,PERC_ASSESSWARN,        B_AssessWarn);
   Npc_PercEnable(self,PERC_ASSESSTALK,        B_AssessTalk);
   Npc_PercEnable(self,PERC_MOVEMOB,           B_MoveMob);
};

//********************************************************************
// Установка функций реакций НПС на минимальный набор восприятий для людей (всегда должны быть активны)
//********************************************************************

func void Perception_Set_Minimal()
{
   self.senses         =   SENSE_HEAR | SENSE_SEE;
   self.senses_range   =   PERC_DIST_ACTIVE_MAX;

   Npc_PercEnable(self,PERC_ASSESSMAGIC,       B_AssessMagic);
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,      B_AssessDamage);
   Npc_PercEnable(self,PERC_ASSESSMURDER,      B_AssessMurder);
   Npc_PercEnable(self,PERC_ASSESSTHEFT,       B_AssessTheft);
   Npc_PercEnable(self,PERC_ASSESSUSEMOB,      B_AssessUseMob);
   Npc_PercEnable(self,PERC_ASSESSENTERROOM,   B_AssessPortalCollision);
};

//************************************************************
// Закрытие реакций НПС slf на восприятия для людей и монстров
//************************************************************

func void B_ClearPerceptions(var c_npc slf)
{
   Npc_PercDisable(slf,PERC_ASSESSPLAYER);
   Npc_PercDisable(slf,PERC_ASSESSENEMY);

   Npc_PercDisable(slf,PERC_ASSESSBODY);

   Npc_PercDisable(slf,PERC_ASSESSMAGIC);
   Npc_PercDisable(slf,PERC_ASSESSDAMAGE);
   Npc_PercDisable(slf,PERC_ASSESSMURDER);
   Npc_PercDisable(slf,PERC_ASSESSTHEFT);
   Npc_PercDisable(slf,PERC_ASSESSUSEMOB);
   Npc_PercDisable(slf,PERC_ASSESSENTERROOM);
   Npc_PercDisable(slf,PERC_ASSESSTHREAT);
   Npc_PercDisable(slf,PERC_DRAWWEAPON);
   Npc_PercDisable(slf,PERC_ASSESSFIGHTSOUND);
   Npc_PercDisable(slf,PERC_ASSESSQUIETSOUND);
   Npc_PercDisable(slf,PERC_ASSESSWARN);
   Npc_PercDisable(slf,PERC_ASSESSTALK);
   Npc_PercDisable(slf,PERC_MOVEMOB);

   Npc_PercDisable(slf,PERC_ASSESSOTHERSDAMAGE);
   Npc_PercDisable(slf,PERC_ASSESSSTOPMAGIC);
   Npc_PercDisable(slf,PERC_ASSESSSURPRISE);
};

//*********************************************************
// Установка функций реакций монстров и орков на восприятия
//*********************************************************

func void Perception_Set_Monster_Rtn()
{
   Npc_SetPercTime(self,1);                //частота проверки восприятия раз в секунду

   Npc_PercEnable(self,PERC_ASSESSENEMY,           B_MM_AssessEnemy);
   Npc_PercEnable(self,PERC_ASSESSBODY,            B_MM_AssessBody);

   Npc_PercEnable(self,PERC_ASSESSMAGIC,           B_AssessMagic);
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,          B_MM_AssessDamage);
   Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,    B_MM_AssessOthersDamage);
   Npc_PercEnable(self,PERC_ASSESSMURDER,          B_MM_AssessOthersDamage);
   Npc_PercEnable(self,PERC_ASSESSWARN,            B_MM_AssessWarn);
};


5. Функции обработки восприятий людей.

//*******************************************************
// Функция реакции НПС (людей) на ГГ (обработка активного восприятия PERC_ASSESSPLAYER).
// ГГ должен быть в поле зрения НПС (люди не имеют чувства обоняния)
// Макс. дистанция реакции на ГГ - PERC_DIST_ACTIVE_MAX.
//*******************************************************

func void B_AssessPlayer()
{
   // other в функции - ГГ, self - НПС
   // Тестовый режим: Игнорирование Инспектора уровня
   var c_npc PCL;
   PCL = Hlp_GetNpc(PC_Levelinspektor);
   // Если ГГ является Инспектором уровня
   if(Hlp_GetInstanceID(other) == Hlp_GetInstanceID(PCL))
   {
       return;
   };
   // Если ГГ находится в режиме диалога
   if(other.aivar[AIV_INVINCIBLE] == TRUE)
   {
       return;
   };
   // Если ГГ мертв, без сознания или в магическом сне
   if(C_NpcIsDown(other))
   {
       return;
   };
   // Если ГГ превращен в монстра
   if(other.guild > GIL_SEPERATOR_HUM)
   {
       // Если НПС охранник, то он должен задержать любого монстра
       if(C_NpcIsGateGuard(self))
       {
           // НПС встает
           AI_StandUpQuick(self);
           // вызов функции сражения
           B_Attack(self,other,AR_MonsterCloseToGate,0);
           return;
       }
       // если гильдии ГГ и НПС враждебны
       else if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_HOSTILE)
       {
           // если НПС не сопровождает ГГ и НПС не является другом ГГ
           if(self.aivar[AIV_PARTYMEMBER] == FALSE) && (self.npctype != NPCTYPE_FRIEND)
           {
               // вызов функции сражения
               B_Attack(self,other,AR_GuildEnemy,0);
               return;
           };
       };
   };
   // Если ГГ враг
   if(B_AssessEnemy())
   {
       return;
   };
   // Если ГГ бандит и НПС не бандит
   if(C_PlayerIsFakeBandit(self,other) && (self.guild != GIL_BDT))
   {
       // вызов функции сражения
       B_Attack(self,other,AR_GuildEnemy,0);
   };
   // Если ГГ убийца и НПС должен атаковать убийцу и расстояние между ГГ и НПС <= PERC_DIST_INTERMEDIAT
   if(B_GetPlayerCrime(self) == CRIME_MURDER) && C_WantToAttackMurder(self,other) && (Npc_GetDistToNpc(self,other) <= PERC_DIST_INTERMEDIAT)
   {
       // вызов функции сражения
       B_Attack(self,other,AR_HumanMurderedHuman,0);
       return;
   };
   // Если ГГ вошел в помещение, ему не принадлежащее
   if(B_AssessEnterRoom())
   {
       return;
   };
   // Если оружие ГГ обнажено
   if(B_AssessDrawWeapon())
   {
       return;
   }
   else // сброс флага комментирования обнаженного оружия
   {
       Player_DrawWeaponComment = FALSE;
   };
   // Если ГГ подкрадывается
   if(C_BodyStateContains(other,BS_SNEAK))
   {
       // если НПС не наблюдает за ГГ и НПС должен реагировать на крадущегося ГГ
       if(!Npc_IsInState(self,ZS_ObservePlayer) && C_WantToReactToSneaker(self,other))
       {
           // очистка AI команд НПС
           Npc_ClearAIQueue(self);
           // очистка восприятий НПС
           B_ClearPerceptions(self);
           // перевод НПС в состояние наблюдения за ГГ
           AI_StartState(self,ZS_ObservePlayer,1,"");
           return;
       };
   }
   else // ГГ не подкрадывается
   {
       // если ГГ не стоит
       if(!C_BodyStateContains(other,BS_STAND))
       {
           // сброс флага комментирования крадущегося ГГ
           Player_SneakerComment = FALSE;
       };
   };
   // Если ГГ не лежит
   if(!C_BodyStateContains(other,BS_LIE))
   {
       // сброс флага комментирования, что ГГ лег на чужую кровать
       Player_GetOutOfMyBedComment = FALSE;
   };
   // Важная информация для окружающих черных магов (я так понимаю, что это ищущие)
   B_AssignDementorTalk(self);
   //*********** Диалог*************
   // Если расстояние между ГГ и НПС <= дистанции начала разговора и НПС имеет важную информацию
   if(Npc_GetDistToNpc(self,other) <= PERC_DIST_DIALOG) && (Npc_CheckInfo(self,1))
   {
       // Если НПС охраняет ворота
       if(C_NpcIsGateGuard(self))
       {
           // установка флага для НПС при обращении с важной информацией к ГГ
           self.aivar[AIV_NpcStartedTalk] = TRUE;
           // функция диалога
           B_AssessTalk();
           return;
       }
       else //для других НПС
       {
           // если ГГ не падает и не плывет и не ныряет
           if(!C_BodyStateContains(other,BS_FALL)) && (!C_BodyStateContains(other,BS_SWIM)) && (!C_BodyStateContains(other,BS_DIVE))
           // и ГГ не занимается криминалом
           && (B_GetPlayerCrime(self) == CRIME_NONE)
           // и ГГ не отказался от разговора с НПС
           && (C_RefuseTalk(self,other) == FALSE)
           // и ГГ не является членом подставной гильдии
           && (C_PlayerHasFakeGuild(self,other) == FALSE)
           {
               // установка флага для НПС при обращении с важной информацией к ГГ
               self.aivar[AIV_NpcStartedTalk] = TRUE;
               // функция диалога
               B_AssessTalk();
               return;
           };
       };
   };
   //*********** Приветствие*************
   // если НПС идет и расстояние между ГГ и НПС <= дистанции начала разговора
   if (C_BodyStateContains(self,BS_WALK)) && (Npc_GetDistToNpc(self,other) <= PERC_DIST_DIALOG)
   // и ГГ не отказался от диалога и НПС не охранник
   && (Npc_RefuseTalk(other) == FALSE) && (!C_NpcIsGateGuard(self))
   // и ГГ не является членом подставной гильдии
   && (C_PlayerHasFakeGuild(self,other) == FALSE)
   {
       // НПС и ГГ смотрят друг на друга
       B_LookAtNpc(self,other);
       // звучит приветственная фраза
       B_Say_GuildGreetings(self,other);
       // НПС и ГГ перестают смотреть друг на друга
       B_StopLookAt(self);
       // отказ ГГ от диалога на 20 секунд
       Npc_SetRefuseTalk(other,20);
   };
   //*********** Сброс предупреждений охраной*************
   // если НПС охранник и расстояние между ГГ и НПС > дистанции начала разговора
   if(C_NpcIsGateGuard(self)) && (Npc_GetDistToNpc(self,other) > PERC_DIST_DIALOG)
   {
       // Статус предупреждений охраны = Нет предупреждений
       self.aivar[AIV_Guardpassage_Status] = GP_NONE;
   };
   return;
};

// ********************************************************************************
// Функция реакции НПС (людей) на врага (обработка активного восприятия PERC_ASSESSENEMY).
// враг должен быть в поле зрения НПС (люди не имеют чувства обоняния)
// Макс. дистанция реакции на врага - PERC_DIST_ACTIVE_MAX.
// ********************************************************************************
// Возвращает TRUE, если реакция НПС на врага закончилась сражением
// ********************************************************************************

func int B_AssessEnemy()
{
   // other в функции - враг, self - НПС
   // Тестовый режим: Игнорирование Инспектора уровня
   var c_npc PCL;
   PCL = Hlp_GetNpc(PC_Levelinspektor);
   // Если враг является Инспектором уровня
   if(Hlp_GetInstanceID(other) == Hlp_GetInstanceID(PCL))
   {
       return FALSE;
   };
   // Аддон
   // Если (враг не ГГ и враг человек)
   if((Hlp_GetInstanceID(other) != Hlp_GetInstanceID(hero)) && (other.guild < GIL_SEPERATOR_HUM)
   // и (НПС не может атаковать или враг не может атаковать))
       && ((self.aivar[AIV_NoFightParker] == TRUE) || (other.aivar[AIV_NoFightParker] == TRUE)))
   // или (враг монстр и враг не может атаковать)
       || ((other.guild > GIL_SEPERATOR_HUM) && (other.aivar[AIV_NoFightParker] == TRUE))
   {
       return FALSE;
   };
   // Аддон
   // Если (враг плывет или враг ныряет) и НПС не хочет лезть в воду
   if(C_BodyStateContains(other,BS_SWIM) || C_BodyStateContains(other,BS_DIVE)) && (self.aivar[AIV_MM_FollowInWater] == FALSE)
   {
       return FALSE;
   };
   // Если расстояние по высоте между НПС и врагом > дистанции реагирования по высоте
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT)
   {
       return FALSE;
   };
   // Если НПС член партии
   if(self.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       // если дистанция между НПС и врагом > 1.5 метра
       if(Npc_GetDistToNpc(self,other) > 1500)
       {
           return FALSE;
       };
       // если НПС не видит врага
       if(!Npc_CanSeeNpc(self, other))
       {
           return FALSE;
       };
   };
   // Если враг является переодетым бандитом и НПС бандит
   if(C_PlayerisFakeBandit(self,other)) && (self.guild == GIL_BDT)
   {
       return FALSE;
   };
   // Магический Голем
   var c_npc MGO; MGO = Hlp_GetNpc(MagicGolem);
   // Ларес
   var c_npc LAR; LAR = Hlp_GetNpc(VLK_449_Lares);
   // Если НПС есть Ларес и враг есть Магический Голем
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(LAR)) && (Hlp_GetInstanceID(other) == Hlp_GetInstanceID(MGO))
   {
       return FALSE;
   };
   // Если (врагов у НПС нет или врагов у врага нет) и враг человек
   if((self.aivar[AIV_EnemyOverride] == TRUE) || (other.aivar[AIV_EnemyOverride] == TRUE)) && (other.guild < GIL_SEPERATOR_HUM)
   {
       return FALSE;
   };
   // Если отношения между НПС и врагом не вражеские
   if(Npc_GetAttitude(self,other) != ATT_HOSTILE)
   {
       return FALSE;
   };
   // Если НПС охраняет ворота
   if(C_NpcIsGateGuard(self))
   {
       return FALSE;
   };

   // Если враг есть ГГ и НПС ему друг
   if(Npc_IsPlayer(other) && (self.npctype == NPCTYPE_FRIEND))
   {
       return FALSE;
   };
   // Если гильдии НПС и врага не враждуют между собой
   if(Wld_GetGuildAttitude(self.guild,other.guild) != ATT_HOSTILE)
   {
       // Если НПС враждебен к врагу [Примечание, далее врагом может быть только ГГ, т.к. прочие НПС не имеют temp_att к другим НПС]
       if(Npc_GetAttitude(self,other) == ATT_HOSTILE)
       // и (НПС находится в текущем состоянии > 2 секунд или НПС наблюдает за ГГ)
       && ((Npc_GetStateTime(self) > 2) || Npc_IsInState(self,ZS_ObservePlayer))
       // и дистанция между НПС и ГГ <= дальности действия пассивного восприятия
       && (Npc_GetDistToNpc(self,other) <= PERC_DIST_INTERMEDIAT)
       {
           // вызов функции сражения
           B_Attack(self, other, self.aivar[AIV_LastPlayerAR], 0);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // В любом другом случае вызов функции сражения
   B_Attack(self,other,AR_GuildEnemy,0);
   return TRUE;
};

// ****************************************************************
// Функция реакции людей на повреждение
// ****************************************************************

func void B_AssessDamage()
{
   // self - жертва, other - агрессор
   var c_npc Quarho;
   // Куарходрон
   Quarho = Hlp_GetNpc(NONE_ADDON_111_Quarhodron);
   var c_npc Rhadem;
   // Радемес
   Rhadem = Hlp_GetNpc(NONE_ADDON_112_Rhademes);
   // Если жертва Куарходрон или Радемес
   if((Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Quarho))) || ((Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Rhadem)))
   {
       // вызов функции реакции на повреждение для призраков
       B_GhostSpecialDamage(other,self);
       return;
   };
   // вызов функции реакции на повреждение "Когтем Белиара"
   B_BeliarsWeaponSpecialDamage(other,self);
   // если жертва Альрик получает повреждение после того, как он сдался
   if(self.aivar[AIV_ArenaFight] == AF_AFTER)
   {
       // ГГ нечестно выиграл сражение с Альриком
       self.aivar[AIV_ArenaFight] = AF_AFTER_PLUS_DAMAGE;
   };
   // если у жертвы врагов не было
   if(self.aivar[AIV_EnemyOverride] == TRUE)
   {
       var c_npc RAV;
       // Равен
       RAV = Hlp_GetNpc(BDT_1090_Addon_Raven);
       // если Равен жертва
       if(Hlp_GetInstanceID(self) == (Hlp_GetInstanceID(RAV)))
       {
           // теперь у Равена есть враг
           self.aivar[AIV_EnemyOverride] = FALSE;
       };
   };
   // если жертва находится в состоянии атаки
   if(Npc_IsInState(self,ZS_Attack))
   {
       // если ГГ агрессор и жертва друг ГГ (Примечание: защита от нанесения повреждений ГГ своему другу, если они вместе с кем-то сражаются)
       if(Npc_IsPlayer(other)) && (self.npctype == NPCTYPE_FRIEND)
       {
           return;
       };
       // если ГГ агрессор и жертва член партии ГГ
       if(Npc_IsPlayer(other)) && (self.aivar[AIV_PARTYMEMBER] == TRUE)
       {
           return;
       };
       // если НПС агрессор не является НПС, атакованным жертвой
       if(Hlp_GetInstanceID(other) != self.aivar[AIV_LASTTARGET])
       {
           // если удар жертве нанесен агрессором не первый раз или агрессор не ГГ
           if(self.aivar[AIV_HitByOtherNpc] == Hlp_GetInstanceID(other)) || (Hlp_GetInstanceID(other) != Hlp_GetInstanceID(hero))
           {
               // смена цели (жертва будет атаковать агрессора)
               Npc_SetTarget(self,other);
           }
           else
           {
               // при первом ударе жертве записывается ID агрессора
               self.aivar[AIV_HitByOtherNpc] = Hlp_GetInstanceID(other);
           };
       };
       return;
   };
   // реакция жертвы на врага
   if(B_AssessEnemy())
   {
       return;
   };
   // если НПС агрессор не ГГ и причины нападать у него не было
   if(!Npc_IsPlayer(other)) && (other.aivar[AIV_ATTACKREASON] == AR_NONE)
   {
       // жертва атакует НПС агрессора
       B_Attack(self,other,AR_NONE,0);
       return;
   };
   // если агрессор нападает с мечом или с кулаками или вышел из режима нападения (спрятал оружие)
   if(Npc_IsInFightMode(other,FMODE_MELEE)) || (Npc_IsInFightMode(other,FMODE_FIST)) || (Npc_IsInFightMode(other,FMODE_NONE))
   {
       // если отношения между агрессором и жертвой дружеские или жертва является другом агрессора ГГ
       if(Npc_GetAttitude(self,other) == ATT_FRIENDLY) || ((self.npctype == NPCTYPE_FRIEND) && Npc_IsPlayer(other))
       {
           // если жертва не находится в состоянии реакции на повреждение
           if(!Npc_IsInState(self,ZS_ReactToDamage))
           {
               // очистка очереди состояний жертвы
               Npc_ClearAIQueue(self);
               // очистка восприятий жертвы
               B_ClearPerceptions(self);
               // перевод жертвы в состояние реакции на повреждение
               AI_StartState(self,ZS_ReactToDamage,0,"");
               return;
           };
       };
   };
   // жертва атакует агрессора
   B_Attack(self,other,AR_ReactToDamage,0);
   return;
};

// ****************************************************
// Функция реакции людей на убийство
// ****************************************************

func void B_AssessMurder()
{
   // self - свидетель убийства, other - преступник (убийца), victim - жертва (труп)
   // если свидетель убийца
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(other))
   {
       return;
   };
   // если расстояния между (свидетелем и преступником) и (свидетелем и жертвой) > макс. дальности действия восприятия
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT) && (Npc_GetDistToNpc(self,victim) > PERC_DIST_INTERMEDIAT)
   {
       return;
   };
   // если расстояния по высоте между (свидетелем и преступником) и (свидетелем и жертвой) > дальности реакций НПС
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT) && (Npc_GetHeightToNpc(self,victim) > PERC_DIST_HEIGHT)
   {
       return;
   };
   // если свидетель не видит преступника и свидетель не видит жертву
   if(!Npc_CanSeeNpcFreeLOS(self,other)) && (!Npc_CanSeeNpcFreeLOS(self,victim))
   {
       return;
   };
   // вызов функции реакции свидетеля на преступника (врага)
   if(B_AssessEnemy())
   {
       return;
   };
   // если жертва овца и она убита не "крутым парнем"
   if(victim.guild == GIL_SHEEP) && (victim.aivar[AIV_ToughGuy] == FALSE)
   {
       // если есть причина свидетелю атаковать преступника
       if(C_WantToAttackSheepKiller(self,other))
       {
           // вызов функции атаки
           B_Attack(self,other,AR_SheepKiller,0);
           return;
       }
       else // в противном случае
       {
           // если свидетель является охранником ворот
           if(C_NpcIsGateGuard(self))
           {
               // свидетель запоминает новость, что преступник убил овцу
               B_MemorizePlayerCrime(self,other,CRIME_SHEEPKILLER);
           };
       };
   };
   // если преступник монстр и жертва монстр
   if(other.guild > GIL_SEPERATOR_HUM) && (victim.guild > GIL_SEPERATOR_HUM)
   {
       return;
   };
   // если преступник монстр
   if(other.guild > GIL_SEPERATOR_HUM)
   {
       // свидетель атакует монстра
       B_Attack(self,other,AR_MonsterMurderedHuman,0);
       return;
   };
   // если жертва монстр
   if(victim.guild > GIL_SEPERATOR_HUM)
   {
       return;
   };
   // если у свидетеля врагов не было
   if(self.aivar[AIV_EnemyOverride] == TRUE)
   {
       // теперь у свидетеля есть враг
       self.aivar[AIV_EnemyOverride] = FALSE;
       // свидетелю разрешено воспринимать все объекты в зоне действия восприятия
       Npc_PerceiveAll(self);
       // поиск свидетелем цели
       Npc_GetNextTarget(self);
       // если преступник существует и он не бессознателен
       if(Hlp_IsValidNpc(other)) && (!C_NpcIsDown(other))
       {
           // свидетель атакует преступника
           B_Attack(self,other,AR_GuildEnemy,0);
           return;
       };
       return;
   };
   // если (свидетель враждебен к жертве или свидетель зол на жертву)
   if((Npc_GetAttitude(self,victim) == ATT_HOSTILE) || (Npc_GetAttitude(self,victim) == ATT_ANGRY))
   // и (свидетель друг преступника или свидетель нейтрален к преступнику)
   && ((Npc_GetAttitude(self,other) == ATT_FRIENDLY) || (Npc_GetAttitude(self,other) == ATT_NEUTRAL))
   {
       return;
   };
   // если преступник ГГ и свидетель его друг
   if(Npc_IsPlayer(other) && (self.npctype == NPCTYPE_FRIEND))
   {
       return;
   };
   // если свидетель не должен реагировать на убийство
   if(!C_WantToAttackMurder(self,other))
   {
       // если свидетель является охранником ворот
       if(C_NpcIsGateGuard(self))
       {
           // свидетель запоминает новость, что преступник убийца
           B_MemorizePlayerCrime(self,other,CRIME_MURDER);
       };
       return;
   };
   // если преступник и жертва являются НПС, которым разрешено убить друг друга
   if(other.aivar[AIV_DropDeadAndKill] == TRUE) || (victim.aivar[AIV_DropDeadAndKill] == TRUE)
   {
       return;
   };
   // если жертва черный маг (ищущий) или (жертва бандит и он не из Лагеря бандитов)
   if(victim.guild == GIL_DMT) || ((victim.guild == GIL_BDT) && !C_NpcBelongsToBL(victim))
   {
       return;
   };
   // свидетель атакует преступника
   B_Attack(self,other,AR_HumanMurderedHuman,0);
   return;
};

// ***********************************************************
// Функция реакции людей на воровство
// ***********************************************************
// Вызывается в качестве реакции на пассивное восприятие PERC_ASSESSTHEFT
// в зоне действия PERC_DIST_INTERMEDIAT когда НПС поднимает какой-либо предмет
// ***********************************************************

func void B_AssessTheft()
{
   // other - вор, self - свидетель
   // если вор не ГГ
   if(!Npc_IsPlayer(other))
   {
       return;
   };
   // если ГГ находится в доме, принадлежащим любой гильдии, и расстояние между свидетелем и вором > дистанции реакции через закрытые двери
   if(Wld_GetPlayerPortalGuild() >= GIL_NONE) && (Npc_GetHeightToNpc(self,other) > PERC_DIST_INDOOR_HEIGHT)
   {
       return;
   };
   // если гильдии вора и свидетеля дружественны
   if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_FRIENDLY)
   {
       // если предмет существует и он принадлежит свидетелю
       if(Hlp_IsValidItem(item)) && (Npc_OwnedByNpc(item,self))
       {
           // продолжаем анализ ситуации дальше
       }
       else
       {
           return;
       };
   };
   // если предмет принадлежал ГГ ранее
   if(!C_IsTakenItemMyPossession(self,other,item))
   {
       return;
   };
   // если свидетель не видит вора
   if(!Npc_CanSeeNpc(self,other))
   {
       // если свидетель и ГГ находится в одном помещении и если (свидетель наблюдает за ГГ или свидетель находится в состоянии "незваного гостя")
       if(Npc_IsInPlayersRoom(self)) && ((Npc_IsInState(self,ZS_ObservePlayer)) || (Npc_IsInState(self,ZS_ClearRoom)))
       {
           // продолжаем анализ ситуации дальше
       }
       else
       {
           return;
       };
   };
   // если свидетель игнорирует вора
   if(!C_WantToAttackThief(self,other))
   {
       // если свидетель является охранником ворот
       if(C_NpcIsGateGuard(self))
       {
           // свидетель запоминает новость, что ГГ вор
           B_MemorizePlayerCrime(self,other,CRIME_THEFT);
       };
       return;
   };
   // свидетель атакует вора
   B_Attack(self,other,AR_Theft,0);
   return;
};

// ****************************************************
// Функция реакции людей на использование МОB's
// ****************************************************

func void B_AssessUseMob()
{
   // other - пользователь, self - свидетель
   // если пользователь не ГГ
   if(!Npc_IsPlayer(other))
   {
       return;
   };
   // если ГГ находится в доме, принадлежащим любой гильдии
   if(Wld_GetPlayerPortalGuild() >= GIL_NONE)
   {
       // если расстояние между свидетелем и пользователем > дистанции реакции через закрытые двери
       if (Npc_GetHeightToNpc(self,other) > PERC_DIST_INDOOR_HEIGHT)
       {
           return;
       };
   };
   // если гильдии пользователя и свидетеля дружественны
   if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_FRIENDLY)
   {
       // если МОВ принадлежит свидетелю
       if(Npc_IsDetectedMobOwnedByNpc(other,self))
       {
           // продолжаем анализ ситуации дальше
       }
       else
       {
           return;
       };
   };
   // если МОВ принадлежал ГГ ранее
   if(!C_IsUsedMobMyPossession(self,other))
   {
       return;
   };
   // если свидетель не видит пользователя
   if(!Npc_CanSeeNpc(self,other))
   {
       // если свидетель и ГГ находятся в одном помещении и если (свидетель наблюдает за ГГ или свидетель находится в состоянии "незваного гостя")
       if(Npc_IsInPlayersRoom(self)) && ((Npc_IsInState(self,ZS_ObservePlayer)) || (Npc_IsInState(self,ZS_ClearRoom)))
       {
           // продолжаем анализ ситуации дальше
       }
       else
       {
           return;
       };
   };
   var string detMob;
   // получить название МОВа, используемого ГГ
   detMob = Npc_GetDetectedMob(other);
   // если МОВ не "Большой сундук" и МОВ не "Маленький сундук"
   if(Hlp_StrCmp(detMob,"CHESTBIG") == FALSE) && (Hlp_StrCmp(detMob,"CHESTSMALL") == FALSE)
   {
       return;
   };
   // если свидетель игнорирует пользователя
   if(!C_WantToAttackThief(self,other))
   {
       // если свидетель является охранником ворот
       if (C_NpcIsGateGuard(self))
       {
           // свидетель запоминает новость, что ГГ вор
           B_MemorizePlayerCrime(self,other,CRIME_THEFT);
       };
       return;
   };
   // свидетель атакует пользователя
   B_Attack(self,other,AR_UseMob,0);
   return;
};

// ********************************************************************************
// Функция реакции людей на вход НПС в помещение
// ----------------------------------------
// Возвращает TRUE при реакции на вошедшего или FALSE, когда на вошедшего не реагируют
// ********************************************************************************

func int B_AssessEnterRoom()
{
   // other - вошедший, self - свидетель
   var int portalguild;
   // получить номер гильдии, которой принадлежит помещение (-1 помещение никому не принадлежит)
   portalguild = Wld_GetPlayerPortalGuild();
   // если вошедший ГГ и комментарий входа уже был и помещение принадлежит какой-либо гильдии и помещение не публичное
   if(Npc_IsPlayer(other)) && (Player_LeftRoomComment == TRUE) && (portalguild > GIL_NONE) && (portalguild != GIL_PUBLIC)
   {
       // сброс комментария входа
       Player_LeftRoomComment = FALSE;
   };
   // если расстояние между вошедшим и свидетелем > 10м
   if(Npc_GetDistToNpc(self,other) > 1000)
   {
       return FALSE;
   };
   // если свидетель и вошедший (ГГ) находятся не в одном помещении и свидетель находится не снаружи помещения
   if(!Npc_IsInPlayersRoom(self)) && (!(Npc_GetPortalGuild(self) < GIL_NONE))
   {
       return FALSE;
   };
   // если свидетель находится в состоянии атаки
   if(Npc_IsInState(self,ZS_Attack))
   {
       return FALSE;
   };
   // если свидетель является охранником ворот
   if(C_NpcIsGateGuard(self))
   {
       return FALSE;
   };
   // если вошедший не ГГ
   if(!Npc_IsPlayer(other))
   {
       return FALSE;
   };
   // если свидетель друг ГГ
   if(self.npctype == NPCTYPE_FRIEND)
   {
       return FALSE;
   };
   // если отношения между свидетелем и ГГ дружеские
   if(Npc_GetAttitude(self,other) == ATT_FRIENDLY)
   {
       return FALSE;
   };
   // если ГГ крадется или ГГ стоит
   if(C_BodyStateContains(other,BS_SNEAK)) || (C_BodyStateContains(other,BS_STAND))
   {
       // если свидетель не видит ГГ и свидетель не наблюдает за ГГ
       if(!Npc_CanSeeNpc(self,other)) && (!Npc_IsInState(self,ZS_ObservePlayer))
       {
           return FALSE;
       };
   };
   // если свидетель не принадлежит к гильдии
   if(self.guild == GIL_NONE)
   {
       return FALSE;
   };
   // если свидетель и ГГ находятся не в одном помещениии и свидетель спит
   if(!Npc_IsInPlayersRoom(self)) && (Npc_IsInState(self,ZS_Sleep))
   {
       return FALSE;
   };
   // если помещение публично и свидетель находится в одном помещении с ГГ
   if(portalguild == GIL_PUBLIC) && (Npc_IsInPlayersRoom(self))
   {
       // если свидетель наблюдает за ГГ
       if(Npc_IsInState(self,ZS_ObservePlayer))
       {
           return FALSE;
       };
       // если свидетель лежит
       if(C_BodyStateContains(self,BS_LIE))
       {
           // свидетель встает (Примечание: мне непонятно как он это делает)
           B_MM_DeSynchronize();
       };
       // если свидетель
       if(Npc_IsInState(self,ZS_Potion_Alchemy))       // занимается алхимией
       || (Npc_IsInState(self,ZS_Read_Bookstand))      // или читает книгу
       || (Npc_IsInState(self,ZS_Sit_Bench))           // или сидит на скамье
       || (Npc_IsInState(self,ZS_Sit_Campfire))        // или сидит у костра
       || (Npc_IsInState(self,ZS_Sit_Chair))           // или сидит на стуле
       || (Npc_IsInState(self,ZS_Sit_Throne))          // или сидит на троне
       || (Npc_IsInState(self,ZS_Sleep))               // или спит
       || (Npc_IsInState(self,ZS_Smalltalk))           // или ведет светскую беседу
       || (Npc_IsInState(self,ZS_Smoke_Joint))         // или курит
       || (Npc_IsInState(self,ZS_Stand_ArmsCrossed))   // или стоит со скрещенными руками
       || (Npc_IsInState(self,ZS_Stand_Drinking))      // или пьет
       || (Npc_IsInState(self,ZS_Stand_Eating))        // или ест
       || (Npc_IsInState(self,ZS_Stand_Guarding))      // или охраняет
       || (Npc_IsInState(self,ZS_Stand_WP))            // или стоит на вайпоинте
       {
           // очистка очереди состояний свидетеля
           Npc_ClearAIQueue(self);
           // очистка восприятий свидетеля
           B_ClearPerceptions(self);
           // если свидетель сидит
           if(C_BodyStateContains(self,BS_SIT))
           {
               // перевод свидетеля в состояние наблюдения за ГГ
               AI_StartState(self,ZS_ObservePlayer,0,"");
           }
           else
           {
               // перевод свидетеля в состояние наблюдения за ГГ (выполняется только функция конца состояния)
               AI_StartState(self,ZS_ObservePlayer,1,"");
           };
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если ГГ находится в частном помещении
   if(C_NpcIsBotheredByPlayerRoomGuild(self))
   {
       Npc_ClearAIQueue(self);
       B_ClearPerceptions(self);
       // перевод свидетеля в состояние "защиты помещения"
       AI_StartState(self,ZS_ClearRoom,1,"");
       return TRUE;
   };
   return FALSE;
};

// *************************************************************
// Функция реакции людей на вход НПС в помещение
// ------------------------------------------------
// Вызывается как реакция на пассивное восприятие PERC_ASSESSENTERROOM
// *************************************************************

func void B_AssessPortalCollision()
{
   // other - вошедший, self - свидетель
   var int formerportalguild;
   // получить номер гильдии, которой принадлежит помещение в котором прежде находился ГГ (-1 помещение никому не принадлежит)
   formerportalguild = Wld_GetFormerPlayerPortalGuild();
   // если есть реакция на вошедшего в помещение
   if(B_AssessEnterRoom())
   {
       return;
   };
   // если свидетель не видит вошедшего и (вошедший крадется или вошедший стоит)
   if(!Npc_CanSeeNpc(self,other)) && (C_BodyStateContains(other,BS_SNEAK) || C_BodyStateContains(other,BS_STAND))
   {
       return;
   };
   // свидетелю разрешено воспринимать все объекты в зоне действия восприятия
   Npc_PerceiveAll(self);
   // если свидетель находит другого НПС в состоянии "защиты помещения"
   if(Wld_DetectNpcEx(self,-1,ZS_ClearRoom,-1,FALSE))
   {
       return;
   };
   // если покинутое ГГ помещение принадлежит гильдии свидетеля или свидетель друг гильдии, которой принадлежит помещение
   if(self.guild == formerportalguild) || (Wld_GetGuildAttitude(self.guild,formerportalguild) == ATT_FRIENDLY)
   {
       // если отношения между гильдиями ГГ и свидетеля дружеские или (вошедший ГГ и друг свидетеля)
       if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_FRIENDLY) || (Npc_IsPlayer(other) && (self.npctype == NPCTYPE_FRIEND))
       {
           return;
       };
       // если свидетель не принадлежит к гильдии
       if(self.guild == GIL_NONE)
       {
           return;
       };
       // если (помещение принадлежит милиции или ополчению) и свидетель является другом этих гильдии
       if((formerportalguild == GIL_MIL) || (formerportalguild == GIL_SLD)) && (Wld_GetGuildAttitude(self.guild,formerportalguild) == ATT_FRIENDLY)
       {
           // свидетель атакует ГГ
           B_Attack(self,other,AR_LeftPortalRoom,0);
           return;
       }
       else // иначе
       {
           // свидетель наблюдает за помещением
           self.aivar[AIV_SeenLeftRoom] = TRUE;
           // очистка очереди состояний свидетеля
           Npc_ClearAIQueue(self);
           // очистка восприятий свидетеля
           B_ClearPerceptions(self);
           // перевод свидетеля в состояние наблюдения за ГГ
           AI_StartState(self,ZS_ObservePlayer,0,"");
           return;
       };
   };
};

// *****************************************************************
// Функция реакции НПС, когда в него целится ГГ оружием дальнего радиуса поражения
// ----------------------------------------------------
// Вызывается как реакция на пассивное восприятие PERC_ASSESSTHREAT
// *****************************************************************

func void B_AssessThreat()
{
   // other - ГГ, self - НПС цель
   // если расстояние между ГГ и целью > макс. дальности действия пассивного восприятия
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT)
   {
       return;
   };
   // если цель не видит ГГ
   if(!Npc_CanSeeNpc(self,other))
   {
       return;
   };
   // если оружие, из которого целится ГГ, не может нанести урон цели
   if(!C_NpcIsBotheredByWeapon(self,other))
   {
       return;
   };
   // очистка очереди состояний цели
   Npc_ClearAIQueue(self);
   // очистка восприятий цели
   B_ClearPerceptions(self);
   // переход цели в состояние реакции на оружие ГГ
   AI_StartState(self,ZS_ReactToWeapon,0,"");
   return;
};

// ***************************************************************
// Функция реакции НПС, когда ГГ достает оружие
// ---------------------------------------------------------------
// Вызывается как реакция на пассивное восприятие PERC_DRAWWEAPON и в некоторых других функциях
// ---------------------------------------------------------------
// Возвращает FALSE, если НПС не реагирует на оружие ГГ, в противном случае - TRUE.
// ***************************************************************

func int B_AssessDrawWeapon()
{
   // other - ГГ, self - НПС
   // если ГГ вышел из режима нападения (убрал оружие)
   if(Npc_IsInFightMode(other,FMODE_NONE))
   {
       return FALSE;
   };
   // если расстояние между ГГ и НПС > дистанции начала диалога
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_DIALOG)
   {
       return FALSE;
   };
   // если оружие ГГ не может нанести урон НПС
   if(!C_NpcIsBotheredByWeapon(self,other))
   {
       return FALSE;
   };
   // если НПС уже находится в состоянии реакции на оружие ГГ
   if(Npc_IsInState(self,ZS_ReactToWeapon))
   {
       return FALSE;
   };
   // если НПС находится в состоянии наблюдения за ГГ и предыдущее состояние НПС было наблюдение за сражением ГГ
   if(Npc_IsInState(self,ZS_ObservePlayer)) && (Npc_WasInState(self,ZS_WatchFight))
   {
       return FALSE;
   };
   // если НПС не видит ГГ
   if(!Npc_CanSeeNpc(self,other))
   {
       return FALSE;
   };
   // очистка очереди состояний НПС
   Npc_ClearAIQueue(self);
   // очистка восприятий НПС
   B_ClearPerceptions(self);
   // переход НПС в состояние реакции на оружие ГГ
   AI_StartState(self,ZS_ReactToWeapon,0,"");
   return TRUE;
};

// ************************************************************
// Функция реакции НПС, наблюдающего сражение двух других НПС
// ------------------------------------------------------------
// Вызывается как реакция на пассивное восприятие PERC_ASSESSFIGHTSOUND
// ************************************************************

func void B_AssessFightSound()
{
   // self - свидетель, other - нападающий, victim - жертва
   // если жертва отсутствует
   if(!Hlp_IsValidNpc(victim))
   {
       return;
   };
   // если расстояния между (свидетелем и жертвой) и (свидетелем и нападающим) > макс. дальности действия пассивного восприятия
   if(Npc_GetDistToNpc(self,victim) > PERC_DIST_INTERMEDIAT) && (Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT)
   {
       // если (свидетель чужеземец или бандит) и (гильдии жертвы и свидетеля одинаковы или гильдии нападающего и свидетеля одинаковы)
       if((self.guild == GIL_OUT) || (self.guild == GIL_BDT)) && ((victim.guild == self.guild) || (other.guild == self.guild))
       {
           // продолжение анализа
       }
       else
       {
           return;
       };
   };
   // если свидетель боится воды
   if(self.aivar[AIV_MM_FollowInWater] == FALSE)
   {
       // если нападающий плывет или ныряет или жертва плывет или ныряет
       if(C_BodyStateContains(other,BS_SWIM) || C_BodyStateContains(other,BS_DIVE)
       || C_BodyStateContains(victim,BS_SWIM) || C_BodyStateContains(victim,BS_DIVE))
       {
           return;
       };
   };
   // если нападающий или жертва являются охранниками крепостных стен
   if(other.fight_tactic == FAI_NAILED) || (victim.fight_tactic == FAI_NAILED)
   {
       return;
   };
   // если расстояние по высоте между свидетелем и нападающим и свидетелем и жертвой > расстояния реакции по высоте
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT) && (Npc_GetHeightToNpc(self,victim) > PERC_DIST_HEIGHT)
   {
       return;
   };
   // если расстояние по высоте между свидетелем и нападающим > 5м и свидетель находится не более чем в 30м от "NW_MONASTERY_PLACE_04" (точка перед входом в главное здание монастыря)
   if(Npc_GetHeightToNpc(self,other) > 500) && (Npc_GetDistToWP(self,"NW_MONASTERY_PLACE_04") <= 3000)
   {
       return;
   };
   // если жертва - наблюдатель или нападающий - наблюдатель (мое примечание: если это условие перенести вторым в теле функции, то общее быстродействие увеличится)
   if(Hlp_GetInstanceID(victim) == Hlp_GetInstanceID(self)) || (Hlp_GetInstanceID(other) == Hlp_GetInstanceID(self))
   {
       return;
   };
   // если жертва овца и она атакована не "крутым парнем"
   if(victim.guild == GIL_SHEEP) && (victim.aivar[AIV_ToughGuy] == FALSE)
   {
       // если есть причина свидетеля атаковать нападающего на овец
       if(C_WantToAttackSheepKiller(self,other))
       {
           // свидетель атакует нападающего на овец
           B_Attack(self,other,AR_SheepKiller,0);
           return;
       }
       else // в противном случае
       {
           // если свидетель охранник ворот
           if(C_NpcIsGateGuard(self))
           {
               // свидетель запоминает новость, что было нападение на овцу
               B_MemorizePlayerCrime(self,other,CRIME_SHEEPKILLER);
               return;
           };
       };
       return;
   };
   // если свидетель охранник ворот
   if(C_NpcIsGateGuard(self))
   {
       return;
   };
   // если нападающий и жертва монстры
   if(other.guild > GIL_SEPERATOR_HUM) && (victim.guild > GIL_SEPERATOR_HUM)
   {
       return;
   };
   // если нападающий монстр и свидетель не враг жертве
   if(other.guild > GIL_SEPERATOR_HUM) && (Npc_GetAttitude(self,victim) != ATT_HOSTILE)
   {
       // свидетель атакует монстра
       B_Attack(self,other,AR_MonsterVsHuman,0);
       return;
   };
   // если жертва монстр и свидетель не враг нападающему и жертва жива
   if(victim.guild > GIL_SEPERATOR_HUM) && (Npc_GetAttitude(self,other) != ATT_HOSTILE) && (!Npc_IsDead(victim))
   {
       // свидетель атакует жертву (помогает нападающему)
       B_Attack(self,victim,AR_MonsterVsHuman,0);
       return;
   };
   // если у свидетеля врагов не было
   if(self.aivar[AIV_EnemyOverride] == TRUE)
   {
       // теперь у свидетеля есть враг
       self.aivar[AIV_EnemyOverride] = FALSE;
       // свидетелю разрешено воспринимать все объекты в зоне действия восприятия
       Npc_PerceiveAll(self);
       // поиск свидетелем цели
       Npc_GetNextTarget(self);
       // если нападающий существует и он не бессознателен
       if(Hlp_IsValidNpc(other)) && (!C_NpcIsDown(other))
       {
           // свидетель атакует нападающего
           B_Attack(self,other,AR_GuildEnemy,0);
           return;
       };
       return;
   };
   // --------------------------------------------------
   // Дальше нападающий и жертва являются только людьми!
   // --------------------------------------------------
   // если нападающий ГГ является переодетым бандитом или жертва ГГ является переодетым бандитом
   if((C_PlayerIsFakeBandit(self,other) == TRUE) || (C_PlayerIsFakeBandit(self,victim) == TRUE))
   // и (нападающий бандит или жертва бандит)
   && ((other.guild == GIL_BDT) || (victim.guild == GIL_BDT))
   {
       // если свидетель бандит
       if(self.guild == GIL_BDT)
       {
           // если свидетель и нападающий являются охранниками Эстебана
           if(self.aivar[AIV_STORYBANDIT_ESTEBAN] == TRUE) && (other.aivar[AIV_STORYBANDIT_ESTEBAN] == TRUE)
           {
               // свидетель атакует жертву
               B_Attack(self,victim,AR_NONE,0);
               return;
           };
           // если свидетель и жертва являются охранниками Эстебана
           if(self.aivar[AIV_STORYBANDIT_ESTEBAN] == TRUE) && (victim.aivar[AIV_STORYBANDIT_ESTEBAN] == TRUE)
           {
               // свидетель атакует нападающего
               B_Attack(self,other,AR_NONE,0);
               return;
           };
           // если нападающий и жертва не имели причины для сражения
           if(other.aivar[AIV_ATTACKREASON] == AR_NONE) && (victim.aivar[AIV_ATTACKREASON] == AR_NONE)
           {
               // очистка очереди состояний свидетеля
               Npc_ClearAIQueue(self);
               // очистка восприятий свидетеля
               B_ClearPerceptions(self);
               // перевод свидетеля в состояние наблюдения на сражением
               AI_StartState(self,ZS_WatchFight,0,"");
               return;
           };
           // если нападающий или жертва являются свободными (с которыми можно сражаться) бандитами
           if(other.aivar[AIV_StoryBandit] == TRUE) || (victim.aivar[AIV_StoryBandit] == TRUE)
           {
               Npc_ClearAIQueue(self);
               B_ClearPerceptions(self);
               // перевод свидетеля в состояние наблюдения на сражением
               AI_StartState(self,ZS_WatchFight,0,"");
               return;
           };
       }
       else // иначе (свидетель не бандит)
       {
           // продолжение анализа
       };
   };
   // если нападающий имел причину для сражения
   if((other.aivar[AIV_ATTACKREASON] == AR_GuardStopsIntruder)     // охрана ворот атакует незваного гостя
      || (other.aivar[AIV_ATTACKREASON] == AR_MonsterCloseToGate)  // охрана ворот атакует враждебного монстра
      || (other.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)  // человек убил человека
      || (other.aivar[AIV_ATTACKREASON] == AR_GuildEnemy)          // НПС является врагом гильдии
      || (other.aivar[AIV_ATTACKREASON] == AR_GuardCalledToKill))  // охрана вызвана по факту убийства
   // и свидетель друг нападающему
   && (Npc_GetAttitude(self,other) == ATT_FRIENDLY)
   {
       // свидетель атакует жертву
       B_Attack(self,victim,AR_GuardCalledToKill,0);
       return;
   };
   // тоже самое, что и выше, только свидетель помогает жертве
   if((victim.aivar[AIV_ATTACKREASON] == AR_GuardStopsIntruder)
      || (victim.aivar[AIV_ATTACKREASON] == AR_MonsterCloseToGate)
      || (victim.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)
      || (victim.aivar[AIV_ATTACKREASON] == AR_GuildEnemy)
      || (victim.aivar[AIV_ATTACKREASON] == AR_GuardCalledToKill))
   && (Npc_GetAttitude(self,victim) == ATT_FRIENDLY)
   {
       B_Attack(self,other,AR_GuardCalledToKill,0);
       return;
   };
   // если нападающий имел причину для сражения
   if((other.aivar[AIV_ATTACKREASON] == AR_GuardStopsFight)        // охрана прекратила атаковать преступника
      || (other.aivar[AIV_ATTACKREASON] == AR_ReactToDamage)       // преступник ранил НПС
      || (other.aivar[AIV_ATTACKREASON] == AR_ReactToWeapon))      // преступник не убрал оружие после двукратного предупреждения или пустился в бега
   // свидетель друг нападающему
   && (Npc_GetAttitude(self,other) == ATT_FRIENDLY)
   {
       // если нападающий ополченец или охотник на драконов или не принадлежит к гильдиям
       if((other.guild == GIL_SLD) || (other.guild == GIL_DJG) || (other.guild == GIL_NONE))
       // и если жертва ополченец или охотник на драконов или не принадлежит к гильдиям
       && ((victim.guild == GIL_SLD) || (victim.guild == GIL_DJG) || (victim.guild == GIL_NONE))
       {
           // продолжение анализа
       }
       // иначе, если нападающий и жертва не ГГ
       else if(!Npc_IsPlayer(other)) && (!Npc_IsPlayer(victim))
       {
           // продолжение анализа
       }
       else // иначе
       {
           // свидетель атакует жертву
           B_Attack(self,victim,AR_GuardStopsFight,0);
           return;
       };
   };
   // тоже самое, что и предыдущий if, только свидетель помогает жертве
   if((victim.aivar[AIV_ATTACKREASON] == AR_GuardStopsFight)
      || (victim.aivar[AIV_ATTACKREASON] == AR_ReactToDamage)
      || (victim.aivar[AIV_ATTACKREASON] == AR_ReactToWeapon))
   && (Npc_GetAttitude(self,victim) == ATT_FRIENDLY)
   {
       if((other.guild == GIL_SLD) || (other.guild == GIL_DJG) || (other.guild == GIL_NONE))
       && ((victim.guild == GIL_SLD) || (victim.guild == GIL_DJG) || (victim.guild == GIL_NONE))
       {
       }
       else if(!Npc_IsPlayer(other)) && (!Npc_IsPlayer(victim))
       {
       }
       else
       {
           // свидетель атакует нападающего
           B_Attack(self,other,AR_GuardStopsFight,0);
           return;
       };
   };
   // если нападающий не имел причины к нападению или причиной было убийство НПС ГГ
   if((other.aivar[AIV_ATTACKREASON] == AR_NONE) || (other.aivar[AIV_ATTACKREASON] == AR_KILL))
   // и если жертва не имела причины к нападению или причиной было убийство НПС ГГ
   && ((victim.aivar[AIV_ATTACKREASON] == AR_NONE) || (victim.aivar[AIV_ATTACKREASON] == AR_KILL))
   // и свидетель милиция
   && (self.guild == GIL_MIL)
   {
       // если свидетель друг нападающему и свидетель не друг жертве
       if(Npc_GetAttitude(self,other) == ATT_FRIENDLY) && (Npc_GetAttitude(self,victim) != ATT_FRIENDLY)
       {
           // свидетель атакует жертву
           B_Attack(self,victim,AR_GuardStopsFight,0);
           return;
       }
       // иначе, если свидетель друг жертве и свидетель не друг нападающему
       else if(Npc_GetAttitude(self,victim) == ATT_FRIENDLY) && (Npc_GetAttitude(self,other) != ATT_FRIENDLY)
       {
           // свидетель атакует нападающего
           B_Attack(self,other,AR_GuardStopsFight,0);
           return;
       }
       else // иначе (или оба друга или оба не друга)
       {
           // если нападающий ГГ
           if(Npc_IsPlayer(other))
           {
               // свидетель атакует жертву
               B_Attack(self,victim,AR_GuardStopsFight,0);
               return;
           }
           // иначе, если жертва ГГ
           else if(Npc_IsPlayer(victim))
           {
               // свидетель атакует нападающего
               B_Attack(self,other,AR_GuardStopsFight,0);
               return;
           }
           else // иначе
           {
               // свидетель атакует нападающего
               B_Attack(self,other,AR_GuardStopsFight,0);
               return;
           };
       };
   };
   // ***** помощь нападающему, сражающемуся с вором *****
   // если нападающий имел причину для сражения
   if((other.aivar[AIV_ATTACKREASON] == AR_UseMob)             // использование чужого МОВа
      || (other.aivar[AIV_ATTACKREASON] == AR_Theft)           // воровство
      || (other.aivar[AIV_ATTACKREASON] == AR_LeftPortalRoom)) // вход в чужое помещение
   // и свидетель друг нападающему
   && (Npc_GetAttitude(self,other) == ATT_FRIENDLY)
   {
       // если у свидетеля есть причина атаковать вора жертву
       if(C_WantToAttackThief(self,victim))
       {
           // свидетель атакует жертву
           B_Attack(self,victim,AR_GuardCalledToThief,0);
       };
       return;
   };
   // ***** помощь жертве, сражающейся с вором *****
   // аналогично предыдущему if
   if((victim.aivar[AIV_ATTACKREASON] == AR_UseMob)
      || (victim.aivar[AIV_ATTACKREASON] == AR_Theft)
      || (victim.aivar[AIV_ATTACKREASON] == AR_LeftPortalRoom))
   && (Npc_GetAttitude(self,victim) == ATT_FRIENDLY)
   {
       if(C_WantToAttackThief(self,other))
       {
           B_Attack(self,other,AR_GuardCalledToThief,0);
       };
       return;
   };
   // если нападающий имел причину для сражения как защита своего помещения и свидетель друг нападающему
   if(other.aivar[AIV_ATTACKREASON] == AR_ClearRoom) && (Npc_GetAttitude(self,other) == ATT_FRIENDLY)
   {
       // свидетель атакует жертву
       B_Attack(self,victim,AR_GuardCalledToRoom,0);
       return;
   };
   // аналогично предыдущему, только свидетель помогает жертве
   if(victim.aivar[AIV_ATTACKREASON] == AR_ClearRoom) && (Npc_GetAttitude(self,victim) == ATT_FRIENDLY)
   {
       B_Attack(self,other,AR_GuardCalledToRoom,0);
       return;
   };
   // если расстояния между (свидетелем и жертвой) и (свидетелем и нападающим) > макс. дальности действия пассивного восприятия
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT) && (Npc_GetDistToNpc(self,victim) > PERC_DIST_INTERMEDIAT)
   {
       return;
   };
   // если свидетель враг нападающему или жертве
   if(Npc_GetAttitude(self,other) == ATT_HOSTILE) || (Npc_GetAttitude(self,victim) == ATT_HOSTILE)
   {
       // если свидетель бандит
       if(self.guild == GIL_BDT)
       {
           // продолжение анализа
       }
       else
       {
           return;
       };
   };
   // если свидетель не видит по прямой жертву
   if(!Npc_CanSeeNpcFreeLOS(self,victim))
   {
       return;
   };
   // очистка очереди состояний свидетеля
   Npc_ClearAIQueue(self);
   // очистка восприятий свидетеля
   B_ClearPerceptions(self);
   // перевод свидетеля в состояние наблюдения на сражением
   AI_StartState(self,ZS_WatchFight,0,"");
   return;
};

// *****************************************************************
// Функция реакции НПС на звук шагов ГГ или на звук упавшего предмета
// -----------------------------------------------------------------
// Вызывается в качестве реакции на пассивное восприятие PERC_ASSESSQUIETSOUND
// *****************************************************************

func void B_AssessQuietSound()
{
   // other - НПС источник шума (ГГ), self - свидетель
   // если источник шума отсутствует
   if(!Hlp_IsValidNpc(other))
   {
       return;
   };
   // если расстояние по высоте между источником шума и свидетелем > дистанции реагирования по высоте
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT)
   {
       return;
   };
   // если ГГ находится в доме, принадлежащим любой гильдии, и расстояние по высоте между свидетелем и ГГ > дистанции реакции через закрытые двери
   if(Wld_GetPlayerPortalGuild() >= GIL_NONE) && (Npc_GetHeightToNpc(self,other) > PERC_DIST_INDOOR_HEIGHT)
   {
       return;
   };
   // если есть реакция на вошедшего в помещение
   if(B_AssessEnterRoom())
   {
       return;
   };
   // если свидетель является охранником ворот
   if(C_NpcIsGateGuard(self))
   {
       return;
   };
   // если ГГ и свидетель не враги и свидетель не имеет для ГГ важной информации
   if(Npc_GetAttitude(self,other) != ATT_HOSTILE) && (Npc_CheckInfo(self,1) == FALSE)
   {
       return;
   };
   // если ГГ и свидетель враги
   if(Npc_GetAttitude(self,other) == ATT_HOSTILE)
   // и (у свидетеля личных врагов не было или (ГГ переодетый бандит и свидетель бандит))
   && ((self.aivar[AIV_EnemyOverride] == TRUE) || (C_PlayerIsFakeBandit(self,other) && (self.guild == GIL_BDT)))
   {
       return;
   };
   // если свидетель видит источник звука (моё примечание: возможно здесь ошибка, необходимо поменять условие на противоположное???)
   if(Npc_CanSeeSource(self))
   {
       return;
   };
   // очистка очереди AI команд свидетеля
   Npc_ClearAIQueue(self);
   // очистка восприятий свидетеля
   B_ClearPerceptions(self);
   // перевод свидетеля в состояние наблюдения за ГГ
   AI_StartState(self,ZS_ObservePlayer,1,"");
   return;
};

// **************************************************************
// Функция реакции НПС на предупреждение
// ---------------------------------------
// Предупреждение посылается только скриптами (вызов функции Npc_SendPassivePerc), ядром не испоьзуется
// **************************************************************

func void B_AssessWarn()
{
   // self - свидетель, other - преступник, victim - жертва
   // если свидетель жертва
   if(Hlp_GetInstanceID(victim) == Hlp_GetInstanceID(self))
   {
       return;
   };
   // если свидетель находится в состоянии реакции на обнаженное оружие или наблюдает за ГГ
   if(Npc_IsInState(self,ZS_ReactToWeapon)) || (Npc_IsInState(self,ZS_ObservePlayer))
   {
       return;
   };
   // если преступник монстр
   if(other.guild > GIL_SEPERATOR_HUM)
   {
       return;
   };
   // если свидетель охранник ворот
   if(C_NpcIsGateGuard(self))
   {
       return;
   };
   // очистка очереди AI команд свидетеля
   Npc_ClearAIQueue(self);
   // очистка восприятий свидетеля
   B_ClearPerceptions(self);
   // перевод свидетеля в состояние наблюдения за ГГ
   AI_StartState(self,ZS_ObservePlayer,0,"");
   return;
};

// ****************************************************
// Функция реакции НПС на важную информацию для разговора
// ----------------------------------------------------
// Вызывается в качестве реакции на пассивное восприятие PERC_ASSESSTALK
// ****************************************************

func void B_AssessTalk()
{
   // other - НПС приемник информации (обычно ГГ), self - НПС источник информации

   // Тестовый кусок (можно выкинуть из скрипта) [не рассматриваю]
   var c_npc PCL;
   // Инспектор уровня
   PCL = Hlp_GetNpc(PC_Levelinspektor);
   var c_npc PCR;
   // Рокфеллер
   PCR = Hlp_GetNpc(PC_Rockefeller);
   // если приемник информации Инспектор уровня или Рокфеллер
   if(Hlp_GetInstanceID(other) == Hlp_GetInstanceID(PCL)) || (Hlp_GetInstanceID(other) == Hlp_GetInstanceID(PCR))
   {
       PrintScreen(ConcatStrings("Stimme: ",IntToString(self.voice)),-1,70,FONT_Screen,2);
       PrintScreen("KEIN HERO!",-1,-1,FONT_Screen,2);
       PrintScreen(IntToString(self.aivar[AIV_FollowDist]),-1,70,FONT_Screen,2);
       if(C_NpcIsInQuarter(self) == Q_KASERNE)
       {
           PrintScreen("Q_KASERNE",-1,30,FONT_Screen,2);
       };
       if(C_NpcIsInQuarter(self) == Q_GALGEN)
       {
           PrintScreen("Q_GALGEN",-1,30,FONT_Screen,2);
       };
       if(C_NpcIsInQuarter(self) == Q_MARKT)
       {
           PrintScreen("Q_MARKT",-1,30,FONT_Screen,2);
       };
       if(C_NpcIsInQuarter(self) == Q_TEMPEL)
       {
           PrintScreen("Q_TEMPEL",-1,30,FONT_Screen,2);
       };
       if(C_NpcIsInQuarter(self) == Q_UNTERSTADT)
       {
           PrintScreen("Q_UNTERSTADT",-1,30,FONT_Screen,2);
       };
       if(C_NpcIsInQuarter(self) == Q_HAFEN)
       {
           PrintScreen("Q_HAFEN",-1,30,FONT_Screen,2);
       };
       if(C_NpcIsInQuarter(self) == Q_OBERSTADT)
       {
           PrintScreen("Q_OBERSTADT",-1,30,FONT_Screen,2);
       };
       return;
   };

   // Нормальное начала функции
   // если источник информации монстр
   if(self.guild > GIL_SEPERATOR_HUM)
   {
       // если монстр не имеет важной информации
       if(Npc_CheckInfo(self,1) == FALSE)
       {
           // если монстру вообще нечего сказать
           if(Npc_CheckInfo(self,0) == FALSE)
           {
               return;
           };
       };
   };
   // если источник информации человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {

       // если НПС источник информации враг ГГ
       if(B_AssessEnemy())
       {
           return;
       };
       // если НПС источник информации знает, что ГГ убийца и у него есть причина атакоывать убийцу
       if(B_GetPlayerCrime(self) == CRIME_MURDER) && (C_WantToAttackMurder(self,other))
       {
           // источник информации атакует ГГ убийцу
           B_Attack(self,other,AR_HumanMurderedHuman,0);
           return;
       };
       // если ГГ переодетый бандит и источник информации не бандит
       if(C_PlayerIsFakeBandit(self,other) == TRUE) && (self.guild != GIL_BDT)
       {
           // источник информации атакует ГГ
           B_Attack(self,other,AR_GuildEnemy,0);
           return;
       };
       // если ГГ отказался от разговора
       if(C_RefuseTalk(self,other))
       {
           // если ГГ член подставной гильдии
           if(C_PlayerHasFakeGuild(self,other)
           {
               // очистка очереди AI состояний источника информации
               Npc_ClearAIQueue(self);
               // источник информации переходит в конечное состояние комментирования подставной гильдии
               AI_StartState(self,ZS_CommentFakeGuild,1,"");
               return;
           }
           else
           {
               // источник информации произносит SVM фразу "Оставь меня в покое!"
               B_Say(self,other,"$NOTNOW");
               return;
           };
       };
   };
   // если ГГ еще не говорил с источником информации
   if(self.aivar[AIV_NpcStartedTalk] == FALSE)
   {
       // если источник идет или бежит
       if(C_BodyStateContains(self,BS_WALK) || C_BodyStateContains(self,BS_RUN))
       {
           // ГГ произносит SVM фразу "Подожди!"
           B_Say(other,self,"$SC_HEYWAITASECOND");
       }
       // иначе, если источник информации не видит ГГ
       else if(!Npc_CanSeeNpc(self,other))
       {
           var int rnd;
           // генерация случайного числа от 0 до 99
           rnd = Hlp_Random(100);
           // ГГ призносит фразу
           if(rnd <= 25)       { B_Say(other,self,"$SC_HEYTURNAROUND");    }   // звучит SVM фраза "Эй ты!"
           else if (rnd <= 50) { B_Say(other,self,"$SC_HEYTURNAROUND02");  }   // звучит SVM фраза "Эй ты!"
           else if (rnd <= 75) { B_Say(other,self,"$SC_HEYTURNAROUND03");  }   // звучит SVM фраза "Эй!"
           else if (rnd <= 99) { B_Say(other,self,"$SC_HEYTURNAROUND04");  };  // звучит SVM фраза "Эй!"
       };
   };
   // очистка очереди AI состояний источника информации
   Npc_ClearAIQueue(self);
   // очистка восприятий источника информации
   B_ClearPerceptions(self);
   // если источник информации сидит
   if(C_BodyStateContains(self,BS_SIT))
   {
       // если ГГ заговорил с источником информации
       if(self.aivar[AIV_NpcStartedTalk] == TRUE)
       {
           // ГГ быстро останавливается
           AI_StandUpQuick(other);
       }
       else // иначе
       {
           // ГГ останавливается
           AI_StandUp(other);
       };
       // если источник информации видит ГГ
       if(Npc_CanSeeNpc(self,other))
       {
           // источник информации переходит в состояние разговора, оставаясь сидеть
           AI_StartState(self,ZS_Talk,0,"");
       }
       else // иначе (не видит ГГ)
       {
           // если источник информации наблюдал за ГГ
           if(Npc_IsInState(self,ZS_ObservePlayer))
           {
               // источник информации встает
               AI_StandUp(self);
           };
           // источник информации переходит в состояние разговора
           AI_StartState(self,ZS_Talk,1,"");
       };
       return;
   }
   else // иначе (не сидит)
   {
       // если ГГ заговорил с источником информации
       if(self.aivar[AIV_NpcStartedTalk] == TRUE)
       {
           // оба быстро останавливаются
           AI_StandUpQuick(self);
           AI_StandUpQuick(other);
       }
       else
       {
           // оба останавливаются
           AI_StandUp(self);
           AI_StandUp(other);
       };
       // источник информации переходит в состояние разговора
       AI_StartState(self,ZS_Talk,0,"");
       return;
   };
};

// **************************************
// Функция реакции НПС на перемещение МОВа
// --------------------------------------
// Вызывается ядром системы как реакция на пассивное восприятие PERC_MOVEMOB
// **************************************

func void B_MoveMob()
{
   // self - НПС перемещающий МОВ (ГГ)
   var string door;
   // получить имя перемещаемого МОВа
   door = Npc_GetDetectedMob(self);
   // если МОВ дверь
   if(Hlp_StrCmp(door,"DOOR"))
   {
       // если дверь закрыта
       if(Wld_GetMobState(self,door) == 0)
       {
           // очистка очереди AI состояний ГГ
           Npc_ClearAIQueue (self);
           // ГГ открывает дверь
           AI_UseMob(self,door,1);
           // зафиксировать состояние двери
           AI_UseMob(self,door,-1);
       };
   }
   else // иначе
   {
       return;
   };
   // ГГ продолжает свои дела
   AI_ContinueRoutine(self);
};
Конец 5 параграфа.
Вот и все основные восприятия, на которые реагируют люди, есть еще дополнительные восприятия, которые инициализируются в функциях состояний, их мы рассмотрим позже.
Все файлы активных и пассивных восприятий людей находятся в директории ..\AI\Human\B_Human\ и имеют имена эквивалентные функциям восприятий. Файл восприятия магии находится в директории ..\AI\Magic\
Примечание: Файл ..\AI\Human\B_Human\B_AssessObserveSuspect.d можно удалить из скриптов, по причине его абсолютной бесполезности.


6. Функции обработки восприятий монстров.

// **************************************************
// Функция реакции монстров на врага.
// --------------------------------------------------
// Вызывается ядром системы в качестве реакции на активное восприятие PERC_ASSESSENEMY.
// **************************************************

func void B_MM_AssessEnemy()
{
   // self - НПС монстр, other - враг
   // если монстр дракон и ГГ имеет "Заряженный глаз Инноса"
   if(self.guild == GIL_DRAGON) && (Npc_HasItems(hero,ItMi_InnosEye_Mis) >= 1)
   {
       return;
   };
   // если монстр или враг не могут атаковать
   if((self.aivar[AIV_NoFightParker] == TRUE) || (other.aivar[AIV_NoFightParker] == TRUE))
   {
       return;
   };
   // если личных врагов у монстра нет и враг человек
   if(self.aivar[AIV_EnemyOverride] == TRUE) && (other.guild < GIL_SEPERATOR_HUM)
   {
       return;
   };
   // ------- TESTMODE: Levelinspektor wird ignoriert ------
   var c_npc PCL;
   // "Инспектор уровня"
   PCL = Hlp_GetNpc(PC_Levelinspektor);
   // если враг "Инспектор уровня"
   if(Hlp_GetInstanceID(other) == Hlp_GetInstanceID(PCL))
   {
       return;
   };
   var c_npc MGO;
   // Магический голем
   MGO = Hlp_GetNpc(MagicGolem);
   var c_npc LAR;
   // Ларес
   LAR = Hlp_GetNpc(VLK_449_Lares);
   // если враг Ларес и монстр Магический голем
   if(Hlp_GetInstanceID(other) == Hlp_GetInstanceID(LAR)) && (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(MGO))
   {
       return;
   };
   // если монстр находится не далее 5м от точки "OC_RAMP_07"
   if(Npc_GetDistToWP(self,"OC_RAMP_07") <= 500)
   {
       return;
   };
   // если враг находится в режиме диалога
   if(other.aivar[AIV_INVINCIBLE] == TRUE)
   {
       return;
   };
   // если враг (плывет или ныряет) и монстр не полезет в воду
   if(C_BodyStateContains(other,BS_SWIM) || C_BodyStateContains(other,BS_DIVE)) && (self.aivar[AIV_MM_FollowInWater] == FALSE)
   {
       return;
   };
   // если расстояние по высоте между монстром и врагом > расстояния реакции по высоте
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT)
   {
       return;
   };
   // если ГГ враг и он превращен в монстра
   if(Npc_IsPlayer(other)) && (other.guild > GIL_SEPERATOR_HUM)
   {
       // если гильдии монстра и превращенного ГГ не враждебны
       if(Wld_GetGuildAttitude(self.guild,other.guild) != ATT_HOSTILE)
       {
           return;
       };
   };
   // если дальность обоняния монстра > макс. дальности действия активного восприятия монстров
   if(self.senses_range > PERC_DIST_MONSTER_ACTIVE_MAX)
   {
       // если расстояние между монстром и врагом > макс. дальности действия активного восприятия монстров
       if(Npc_GetDistToNpc(self,other) > PERC_DIST_MONSTER_ACTIVE_MAX)
       {
           // если монстр не видит врага
           if(!Npc_CanSeeNpc(self,other))
           {
               return;
           };
       };
   };
   // если монстр орк или дружественный орк
   if(self.guild == GIL_ORC) || (self.guild == GIL_FRIENDLY_ORC)
   {
       // если враг подкрадавается или стоит
       if(C_BodyStateContains(other,BS_SNEAK)) || (C_BodyStateContains(other,BS_STAND))
       {
           // если монстр не видит врага
           if(!Npc_CanSeeNpc(self,other))
           {
               return;
           };
       };
   };
   // если монстр не видит врага по прямой (например, враг за стеной)
   if(!Npc_CanSeeNpcFreeLOS(self,other))
   {
       return;
   };
   // если монстр является членом партии (вызванный)
   if(self.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       // если враг монстра является другом НПС
       if(other.npctype == NPCTYPE_FRIEND)
       {
           return;
       };
       // очистка очереди AI состояний монстра
       Npc_ClearAIQueue(self);
       // монстр выбирает цель
       Npc_SetTarget(self,other);
       // очистка восприятий монстра
       B_ClearPerceptions(self);
       // переход монстра в состояние атаки
       AI_StartState(self,ZS_MM_Attack,0,"");
       return;
   };
   // если монстр не угрожает перед атакой (атакует сразу)
   if(self.aivar[AIV_MM_ThreatenBeforeAttack] == FALSE)
   {
       Npc_ClearAIQueue(self);
       Npc_SetTarget(self,other);
       B_ClearPerceptions(self);
       // переход монстра в состояние атаки
       AI_StartState(self,ZS_MM_Attack,0,"");
       return;
   };
   // если монстр находится в состоянии "пожирания падали"
   if(Npc_IsInState(self,ZS_MM_EatBody))
   {
       // если расстояние между монстром и врагом <= дистанции атаки монстров
       if(Npc_GetDistToNpc(self,other) <= FIGHT_DIST_MONSTER_ATTACKRANGE)
       {
           Npc_ClearAIQueue(self);
           Npc_SetTarget(self,other);
           B_ClearPerceptions(self);
           // переход монстра в состояние атаки
           AI_StartState(self,ZS_MM_Attack,0,"");
           return;
       };
       return;
   };
   // если монстр находится в соостоянии охоты
   if(Npc_IsInState(self,ZS_MM_Hunt))
   {
       // если расстояние между монстром и врагом <= дистанции атаки монстров
       if(Npc_GetDistToNpc(self,other) <= FIGHT_DIST_MONSTER_ATTACKRANGE)
       {
           Npc_ClearAIQueue(self);
           Npc_SetTarget(self,other);
           B_ClearPerceptions(self);
           // переход монстра в состояние атаки
           AI_StartState(self,ZS_MM_Attack,0,"");
           return;
       }
       else
       {
           return;
       };
   };
   // если монстра устраивает добыча в виде врага
   if(C_PredatorFoundPrey(self,other))
   {
       Npc_ClearAIQueue(self);
       Npc_SetTarget(self,other);
       B_ClearPerceptions(self);
       // переход монстра в состояние охоты на врага
       AI_StartState(self,ZS_MM_Hunt,0,"");
       return;
   };
   // если врага устраивает добыча в виде монстра
   if(C_PredatorFoundPrey(other,self))
   {
       // если враг находится в соостоянии охоты и если расстояние между монстром и врагом <= дистанции преследования
       if(!Npc_IsInState(other,ZS_MM_Hunt)) && (Npc_GetDistToNpc(self,other) <= FIGHT_DIST_MONSTER_FLEE)
       {
           Npc_ClearAIQueue(self);
           Npc_SetTarget(self,other);
           B_ClearPerceptions(self);
           // переход монстра в состояние преследования
           AI_StartState(self,ZS_MM_Flee,0,"");
           return;
       }
       else
       {
           return;
       };
   };
   // очистка очереди AI состояний монстра
   Npc_ClearAIQueue(self);
   // очистка восприятий монстра
   B_ClearPerceptions(self);
   // переход монстра в состояние угрозы
   AI_StartState(self,ZS_MM_ThreatenEnemy,0,"");
   return;
};

// ******************************************
// Функция реакции монстров на мертвое тело
// ------------------------------------------
// Вызывается ядром системы в качестве реакции на пассивное восприятие PERC_ASSESSBODY
// ******************************************

func void B_MM_AssessBody()
{
   // self - монстр, other - труп
   // если монстр принадлежит к оркам
   if(self.guild > GIL_SEPERATOR_ORC)
   {
       return;
   };
   // если расстояние по высоте между монстром и трупом > расстояния реакции по высоте
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT)
   {
       return;
   };
   // если приоритетом монстра является нападение
   if(self.aivar[AIV_MM_PRIORITY] == PRIO_ATTACK)
   {
       return;
   };
   // если труп не нравится монстру
   if(!C_WantToEat(self,other))
   {
       return;
   };
   // если монстр находится в состоянии атаки
   if(Npc_IsInState(self,ZS_MM_Attack))
   {
       var c_npc stoerenfried;
       // последний атакованный НПС
       stoerenfried = Hlp_GetNpc(self.aivar[AIV_LASTTARGET]);
       // если расстояние между атакованным НПС и трупом <= дистанции атаки монстров
       if(Npc_GetDistToNpc(stoerenfried,other) <= FIGHT_DIST_MONSTER_ATTACKRANGE)
       {
           return;
       };
   };
   // очистка очереди AI состояний монстра
   Npc_ClearAIQueue(self);
   // очистка восприятий монстра
   B_ClearPerceptions(self);
   // переход в состояние пожирания трупа
   AI_StartState(self,ZS_MM_EatBody,0,"");
   return;
};

// ********************************************************
// Функция реакции монстра на повреждение
// --------------------------------------------------------
// Вызывается ядром системы в качестве реакции на пассивное восприятие PERC_ASSESSDAMAGE
// ********************************************************

func void B_MM_AssessDamage()
{
   // self - монстр жертва, other - НПС агрессор
   // установить монстру приоритет на атаку
   self.aivar[AIV_MM_PRIORITY] = PRIO_ATTACK;
   // вызов функции реакции на повреждение "Когтем Белиара"
   B_BeliarsWeaponSpecialDamage(other,self);
   // если агрессор имеет "Святой молот"
   if(Npc_HasItems(other,Holy_Hammer_MIS) > 0)
   {
       var c_npc MagGol;
       // Магический голем
       MagGol = Hlp_GetNpc(MagicGolem);
       // проинициализировать глобальную переменную item "Святым молотом"
       Npc_GetInvItem(other,Holy_Hammer_MIS);
       var c_item OthWeap;
       // оружие, которое агрессор держит в руке
       OthWeap = Npc_GetReadiedWeapon(other);
       // если жертва Магический голем и оружие у агрессора в руке "Святой молот"
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(MagGol)) && (Hlp_GetInstanceID(OthWeap) == Hlp_GetInstanceID(item))
       {
           // Жизнь Магического голема -= 1000 LP
           Npc_ChangeAttribute(self,ATR_HITPOINTS,-1000);
           return;
       };
   };
   // если жертва Каменный сторож и у него нет врагов
   if(self.guild == GIL_STONEGUARDIAN) && (self.aivar[AIV_EnemyOverride] == TRUE)
   {
       // пробуждение Каменного сторожа
       B_AWAKE_STONEGUARDIAN(self);
   };
   // если агрессора устраивает добыча в виде жертвы
   if(C_PredatorFoundPrey(other,self))
   {
       // очистка очереди AI состояний жертвы
       Npc_ClearAIQueue(self);
       // установка цели для жертвы
       Npc_SetTarget(self,other);
       // очистка восприятий жертвы
       B_ClearPerceptions(self);
       // жертва убегает от агрессора
       AI_StartState(self,ZS_MM_Flee,0,"");
       return;
   };
   // если жертва находится в состоянии атаки
   if(Npc_IsInState(self,ZS_MM_Attack))
   {
       // если агрессор ГГ и жертва член партии
       if(Npc_IsPlayer(other)) && (self.aivar[AIV_PARTYMEMBER] == TRUE)
       {
           return;
       };
       // если жертва скелет и агрессор маг скелетов
       if(self.aivar[AIV_MM_REAL_ID] == ID_SKELETON) && (other.aivar[AIV_MM_REAL_ID] == ID_SKELETON_MAGE)
       {
           return;
       };
       // если агрессор не является НПС, атакованным жертвой
       if(Hlp_GetInstanceID(other) != self.aivar[AIV_LASTTARGET])
       {
           // если удар жертве нанесен агрессором не первый раз
           if(self.aivar[AIV_HitByOtherNpc] == Hlp_GetInstanceID(other))
           {
               // смена цели (жертва будет атаковать агрессора)
               Npc_SetTarget(self,other);
           }
           else
           {
               // при первом ударе жертве записывается ID агрессора
               self.aivar[AIV_HitByOtherNpc] = Hlp_GetInstanceID(other);
           };
       };
       return;
   };
   // очистка очереди AI состояний жертвы
   Npc_ClearAIQueue(self);
   // установка цели для жертвы
   Npc_SetTarget(self,other);
   // очистка восприятий жертвы
   B_ClearPerceptions(self);
   // жертва атакует агрессора
   AI_StartState(self,ZS_MM_Attack,0,"");
   return;
};

// ****************************************************************
// Функция реакции монстра на убийство и на повреждение других НПС
// ----------------------------------------------------------------
// Вызывается в качестве реакции на пассивные восприятия PERC_ASSESSOTHERSDAMAGE и PERC_ASSESSMURDER
// ****************************************************************

func void B_MM_AssessOthersDamage()
{
   // self - монстр наблюдатель, other - агрессор, victim - жертва (труп)
   // если расстояния между (наблюдателем и жертвой) и (наблюдателем и агрессором) > макс. дальности действия восприятия
   if(Npc_GetDistToNpc(self,victim) > PERC_DIST_INTERMEDIAT) && (Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT)
   {
       return;
   };
   // если наблюдатель не видит жертву
   if(!Npc_CanSeeNpcFreeLOS(self,victim))
   {
       return;
   };
   // если наблюдатель член партии
   if(self.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       // если жертва ГГ
       if(Npc_IsPlayer(victim))
       {
           // очистка очереди AI состояний наблюдателя
           Npc_ClearAIQueue(self);
           // очистка восприятий наблюдателя
           B_ClearPerceptions(self);
           // установка в качестве цели агрессора
           Npc_SetTarget(self,other);
           // монстр наблюдатель атакует агрессора
           AI_StartState(self,ZS_MM_Attack,0,"");
           return;
       };
       // если агрессор ГГ и жертва жива
       if(Npc_IsPlayer(other)) && (!Npc_IsDead(victim))
       {
           // очистка очереди AI состояний наблюдателя
           Npc_ClearAIQueue(self);
           // очистка восприятий наблюдателя
           B_ClearPerceptions(self);
           // установка в качестве цели жертвы
           Npc_SetTarget(self,victim);
           // монстр наблюдатель атакует жертву
           AI_StartState(self,ZS_MM_Attack,0,"");
           return;
       };
   };
   // если наблюдатель волк
   if(self.guild == GIL_WOLF)
   {
       // если жертва волк и агрессор волк и волк агрессор есть ГГ и жертва убита
       if(victim.guild == GIL_WOLF) && (other.guild == GIL_WOLF) && Npc_IsPlayer(other) && Npc_IsDead(victim)
       {
           Npc_ClearAIQueue(self);
           B_ClearPerceptions(self);
           // перевод наблюдателя в состояние вызванного монстра
           self.start_aistate = ZS_MM_Rtn_Summoned;
           AI_StartState(self,ZS_MM_Rtn_Summoned,0,"");
           return;
       };
   };
   // если наблюдатель каменный страж и жертва каменный страж и у наблюдателя нет врагов
   if(self.guild == GIL_STONEGUARDIAN) && (victim.guild == GIL_STONEGUARDIAN) && (self.aivar[AIV_EnemyOverride] == TRUE)
   {
       // пробуждение каменного стража наблюдателя
       B_AWAKE_STONEGUARDIAN(self);
   };
   // если гильдии наблюдателя и жертвы дружественны и гильдии наблюдателя и агрессора не дружественные
   if(Wld_GetGuildAttitude(self.guild,victim.guild) == ATT_FRIENDLY) && (Wld_GetGuildAttitude(self.guild,other.guild) != ATT_FRIENDLY)
   {
       Npc_ClearAIQueue(self);
       B_ClearPerceptions(self);
       Npc_SetTarget(self,other);
       // наблюдатель атакует агрессора
       AI_StartState(self,ZS_MM_Attack,0,"");
       return;
   };
   // если гильдии наблюдателя и агрессора дружественны и гильдии наблюдателя и жертвы не дружественные
   if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_FRIENDLY) && (Wld_GetGuildAttitude(self.guild,victim.guild) != ATT_FRIENDLY)
   // и жертва не мертва
   && (!Npc_IsDead(victim))
   {
       Npc_ClearAIQueue(self);
       B_ClearPerceptions(self);
       Npc_SetTarget(self,victim);
       // наблюдатель атакует жертву
       AI_StartState(self,ZS_MM_Attack,0,"");
       return;
   };
};

// ******************************************************
// Функция реакции монстров на предупреждение
// ------------------------------------------------------
// Предупреждение посылается только скриптами (вызов функции Npc_SendPassivePerc), ядром не испоьзуется
// ******************************************************

func void B_MM_AssessWarn()
{
   // self - наблюдатель, other - агрессор, victim - жертва
   // если наблюдатель орк
   if(self.guild > GIL_SEPERATOR_ORC)
   {
       // если наблюдатель находится в состоянии атаки
       if(Npc_IsInState(self,ZS_MM_Attack))
       {
           return;
       };
       // если гильдия наблюдателя дружественна гильдии агрессора
       if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_FRIENDLY)
       {
           // очистка очереди AI команд наблюдателя
           Npc_ClearAIQueue(self);
           // установка жертвы в качестве цели наблюдателю
           Npc_SetTarget(self,victim);
           // очистка восприятий наблюдателя
           B_ClearPerceptions(self);
           // наблюдатель атакует жертву
           AI_StartState(self,ZS_MM_Attack,0,"");
           return;
       };
       return;
   };
   // если агрессор человек
   if(other.guild < GIL_SEPERATOR_HUM)
   {
       return;
   };
   // если расстояние по высоте между наблюдателем и агрессором > дистанции реагирования по высоте
   if(Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT)
   {
       return;
   };
   // если наблюдатель находится в состоянии атаки
   if(Npc_IsInState(self,ZS_MM_Attack))
   {
       // если наблюдателя устраивает добыча в виде агрессора и приоритет наблюдателя "пожирание падали"
       if(C_PredatorFoundPrey(self,other)) && (self.aivar[AIV_MM_PRIORITY] == PRIO_EAT)
       {
           // очистка очереди AI команд наблюдателя
           Npc_ClearAIQueue(self);
           // установка агрессора в качестве цели наблюдателю
           Npc_SetTarget(self,other);
       }
       else // иначе цель не меняется
       {
           // возврат цели для наблюдателя (последняя бывшая цель)
           other = Hlp_GetNpc(self.aivar[AIV_LASTTARGET]);
       };
       return;
   };
   // если гильдии наблюдателя и агрессора одинаковы и наблюдатель охотится стаей
   if(self.guild == other.guild) && (self.aivar[AIV_MM_Packhunter] == TRUE)
   {
       // если наблюдатель волк и жертва волк и жертва ГГ (превращенный в волка)
       if(self.guild == GIL_WOLF) && (victim.guild == GIL_WOLF) && Npc_IsPlayer(victim)
       {
           return;
       };
       // если агрессор находится в состоянии атаки
       if(Npc_IsInState(other,ZS_MM_Attack))
       {
           Npc_ClearAIQueue(self);
           Npc_SetTarget(self,victim);
           B_ClearPerceptions(self);
           // наблюдатель атакует жертву
           AI_StartState(self,ZS_MM_Attack,0,"");
           return;
       };
       // если агрессор находится в состоянии угрозы врагу
       if(Npc_IsInState(other,ZS_MM_ThreatenEnemy))
       {
           // агрессор бежит
           AI_SetWalkmode(self,NPC_RUN);
           // к жертве
           AI_GotoNpc(self,victim);
           return;
       };
   };
   return;
};

// ********************************************************
// Функция реакции монстров на ГГ
// --------------------------------------------------------
// Вызывается ядром системы в качестве реакции на активное восприятие PERC_ASSESSPLAYER
// Используется только конкретными монстрами
// ********************************************************

func void B_MM_AssessPlayer()
{
   // other - ГГ, self - монстр
   // если ГГ в состоянии диалога
   if(other.aivar[AIV_INVINCIBLE] == TRUE)
   {
       return;
   };
   // если ГГ мертв, без сознания и т.д. (обездвижен)
   if(C_NpcIsDown(other))
   {
       return;
   };
   // подготовка разговора ГГ с драконами
   B_AssignDragonTalk(self);
   // если расстояние между ГГ и монстром < 7м и монстр имеет важную информацию
   if(Npc_GetDistToNpc(self,other) <= 700) && (Npc_CheckInfo(self,1))
   {
       // если монстр дракон или (монстр не дракон и расстояние между монстром и ГГ <= дистанции начала диалога)
       if(self.guild == GIL_DRAGON) || ((self.guild != GIL_DRAGON) && (Npc_GetDistToNpc(self,other) <= PERC_DIST_DIALOG))
       {
           // если ГГ не падает и не плывет и не ныряет
           if(!C_BodyStateContains(other,BS_FALL)) && (!C_BodyStateContains(other,BS_SWIM)) && (!C_BodyStateContains(other,BS_DIVE))
           {
               // у монстра устанавливается флаг начала разговора
               self.aivar[AIV_NpcStartedTalk] = TRUE;
               // реакция ГГ на предлагаемый разговор
               B_AssessTalk();
               return;
           };
       };
   };
   // если монстр член партии ГГ
   if(self.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       // если расстояние между ГГ и монстром < 5м и монстр не стоит
       if(Npc_GetDistToNpc(self,hero) < 500) && (!C_BodyStateContains(self,BS_STAND))
       {
           // очистка очереди AI состояний монстра
           Npc_ClearAIQueue(self);
           // монстр останавливается
           AI_StandUp(self);
           // и поворачивается к ГГ
           AI_TurnToNpc(self,hero);
       };
   };
};
Все файлы активных и пассивных восприятий монстров находятся в директории ..\AI\Monster\B_Monster\ и имеют имена, эквивалентные функциям восприятий.


7. Описание обработчиков состояний НПС.

Все имена функций состояний должны начинаться с ZS_.
Каждый процесс обработки соответствующего состояния состоит из 3х функций: ZS_xxx, ZS_xxx-Loop, ZS_xxx_End, где ххх - имя соответствующего состояния.
Этим функциям соответствуют 3 фазы одного состояния НПС.
1 фаза - Begin: инициализация состояния НПС.
2 фаза - Loop: цикл выполнения последовательности действий НПС. Функция 2 фазы должна возвращать или LOOP_CONTINUE - продолжение фазы или LOOP_END - конец фазы.
3 фаза - End: завершение состояния НПС.
Системная функция AI_StartState может управлять последовательностью выполнения фаз состояний, за это отвечает параметр stateBehaviour, если он = 0, то выполняются все фазы состояния последовательно, если он = 1, то выполняется только заключительная фаза 3.
Эта функция подробно описана в соответствующей теме.


8. Функции состояний людей.

Все файлы состояний людей расположены в директории ..AI\Human\ZS_Human
// ******************************
// Состояние атаки
// ******************************

// Функция обработки вспомогательного восприятия PERC_ASSESSSURPRISE
// Вызывается ядром системы когда ГГ превращается в человека из животного
func void B_AssessSurprise()
{
   // self - наблюдатель, other - ГГ
   // целью наблюдателя становится ГГ
   Npc_SetTarget(self,other);
   // причина нападения наблюдателя на ГГ - ГГ враг гильдии
   self.aivar[AIV_ATTACKREASON] = AR_GuildEnemy;
};

// Функция инициализации состояния атаки
func void ZS_Attack()
{
   // установить минимальный набор восприятий НПС
   Perception_Set_Minimal();
   // установить дополнительное восприятие на превращение
   Npc_PercEnable(self,PERC_ASSESSSURPRISE,B_AssessSurprise);
   // инициализировать переменную other (последняя цель)
   B_ValidateOther();
   // записать НПС id последней цели
   self.aivar[AIV_LASTTARGET] = Hlp_GetInstanceID(other);
   // если НПС должен убегать от цели
   if(C_WantToFlee(self,other))
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // очистка восприятий НПС
       B_ClearPerceptions(self);
       // установка цели для НПС
       Npc_SetTarget(self,other);
       // НПС убегает от цели
       AI_StartState(self,ZS_Flee,0,"");
       return;
   };
   // если загрузки игры во время атаки не было
   if(self.aivar[AIV_LOADGAME] == FALSE)
   {
       // НПС называет причину атаки
       B_Say_AttackReason();
   };
   // если режим сражения у НПС выключен
   if(Npc_IsInFightMode(self,FMODE_NONE))
   {
       // НПС достает лучшее оружие дальнего радиуса поражения
       AI_EquipBestRangedWeapon(self);
       // НПС достает лучшее оружие ближнего радиуса поражения
       AI_EquipBestMeleeWeapon(self);
   };
   // НПС встает
   AI_StandUp(self);
   // перестает смотреть на других
   B_StopLookAt(self);
   // поворачивается к цели
   B_TurnToNpc(self,other);
   // бежит к цели
   AI_SetWalkmode(self,NPC_RUN);
   // сброс статуса предупреждений охраной
   self.aivar[AIV_Guardpassage_Status] = GP_NONE;
   // получить репутацию ГГ для конкретной локации
   self.aivar[AIV_LastAbsolutionLevel] = B_GetCurrentAbsolutionLevel(self);
   // сброс флага окончания преследования
   self.aivar[AIV_PursuitEnd] = FALSE;
   // сброс времени нахождения в состоянии
   self.aivar[AIV_StateTime] = 0;
   // сброс переменной вызова охраны (переменная AIV_TAPOSITION переопределена в этом состоянии)
   self.aivar[AIV_TAPOSITION] = 0;
   // сброс флага первого удара
   self.aivar[AIV_HitByOtherNpc] = 0;
   // сброс выбранного заклинания
   self.aivar[AIV_SelectSpell] = 0;
};

// Функция цикла атаки
func int ZS_Attack_Loop()
{
   // Грег идет к Декстеру
   B_Greg_ComesToDexter();
   // повторно инициализировать other целью
   Npc_GetTarget(self);
   // если расстояние между НПС и целью > дистанции прекращения сражения
   if(Npc_GetDistToNpc(self,other) > self.aivar[AIV_FightDistCancel])
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       // НПС закончил преследование
       self.aivar[AIV_PursuitEnd] = TRUE;
       return LOOP_END;
   };
   // если время преследования превышает допустимое и НПС преследует
   if(Npc_GetStateTime(self) > self.aivar[AIV_MM_FollowTime]) && (self.aivar[AIV_PursuitEnd] == FALSE)
   {
       Npc_ClearAIQueue(self);
       AI_StandUp(self);
       // НПС закончил преследование
       self.aivar[AIV_PursuitEnd] = TRUE;
       // получить дистанцию до цели
       self.aivar[AIV_Dist] = Npc_GetDistToNpc(self,other);
       // обновить время состояния НПС
       self.aivar[AIV_StateTime] = Npc_GetStateTime(self);
       // если цель человек
       if(other.guild < GIL_SEPERATOR_HUM)
       {
           // НПС говорит цели "Беги, трус!" (SVM фраза)
           B_Say(self,other,"$RUNCOWARD");
       };
   };
   // если НПС завершил преследование цели
   if(self.aivar[AIV_PursuitEnd] == TRUE)
   {
       // если расстояние между НПС и целью > диапазона чувствительности НПС
       if(Npc_GetDistToNpc(self,other) > (self.senses_range))
       {
           return LOOP_END;
       };
       // если текущее время > предыдущего времени (прошла как минимум секунда)
       if(Npc_GetStateTime(self) > self.aivar[AIV_StateTime])
       {
           // если расстояние между НПС и целью меньше дистанции, когда НПС прекратил преследование
           if(Npc_GetDistToNpc(self,other) < self.aivar[AIV_Dist])
           // или (цель не бежит и не прыгает)
           || ((!C_BodyStateContains(other,BS_RUN)) && (!C_BodyStateContains(other,BS_JUMP)))
           {
               // сброс флага окончания преследования
               self.aivar[AIV_PursuitEnd] = FALSE;
               // сброс времени нахождения НПС в этом состоянии
               Npc_SetStateTime(self,0);
               // сброс времени нахождения в состоянии
               self.aivar[AIV_StateTime] = 0;
           }
           else
           {
               // НПС поворачивается к цели
               B_TurnToNpc(self,other);
               // получить дистанцию до цели
               self.aivar[AIV_Dist] = Npc_GetDistToNpc(self,other);
               // обновить время состояния НПС
               self.aivar[AIV_StateTime] = Npc_GetStateTime(self);
           };
       };
       return LOOP_CONTINUE;
   };
   // если уровень достигнутый ГГ в сражении > первоначального уровня
   if(B_GetCurrentAbsolutionLevel(self) > self.aivar[AIV_LastAbsolutionLevel])
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       // прекращение атаки
       return LOOP_END;
   };
   // если (цель плывет или ныряет) и НПС не преследует цель в воде
   if(C_BodyStateContains(other,BS_SWIM) || C_BodyStateContains(other,BS_DIVE)) && (self.aivar[AIV_MM_FollowInWater] == FALSE)
   {
       Npc_ClearAIQueue(self);
       AI_StandUp(self);
       // НПС закончил преследование
       self.aivar[AIV_PursuitEnd] = TRUE;
       return LOOP_END;
   };
   // если НПС выжидает перед атакой
   if(self.aivar[AIV_WaitBeforeAttack] >= 1)
   {
       // ожидание 0.8 сек
       AI_Wait(self,0.8 );
       // сброс флага задержки
       self.aivar[AIV_WaitBeforeAttack] = 0;
   };
   // если дистанция атаки > 0
   if(self.aivar[AIV_MaxDistToWp] > 0)
   {
       // если расстояние между НПС и вайпоинтом НПС > дистанции атаки и расстояние между целью и вайпоинтом НПС > дистанции акаки
       if(Npc_GetDistToWP(self,self.wp) > self.aivar[AIV_MaxDistToWp]) && (Npc_GetDistToWP(other,self.wp) > self.aivar[AIV_MaxDistToWp])
       {
           // установить НПС тактику защитников крепостных стен
           self.fight_tactic = FAI_NAILED;
       }
       else // иначе (если НПС или цель близка к вайпоинту НПС)
       {
           // установить НПС родную тактику сражения
           self.fight_tactic = self.aivar[AIV_OriginalFightTactic];
       };
   };
   // если цель не бежит и не прыгает
   if((!C_BodyStateContains(other,BS_RUN)) && (!C_BodyStateContains(other,BS_JUMP)))
   {
       // сброс времени текущего состояния
       Npc_SetStateTime(self,0);
   };
   // если время в состоянии > 2 сек (от последнего сброса) и охрана не вызывалась
   if(Npc_GetStateTime(self) > 2) && (self.aivar[AIV_TAPOSITION] == 0)
   {
       // вызов охраны
       B_CallGuards();
       // установка следующей фазы вызова охраны
       self.aivar[AIV_TAPOSITION] = 1;
   };
   // если время в состоянии > 8 сек и охрана вызывалась
   if(Npc_GetStateTime(self) > 8 ) && (self.aivar[AIV_TAPOSITION] == 1)
   {
       // вызов охраны
       B_CallGuards();
       // установка следующей фазы вызова охраны (было 2 вызова)
       self.aivar[AIV_TAPOSITION] = 2;
   };
   // подготовка НПС боезапаса
   B_CreateAmmo(self);
   // выбор соответствующего цели оружия
   B_SelectWeapon(self,other);
   // если цель существует и не обездвижена
   if((Hlp_IsValidNpc(other)) && (C_NpcIsDown(other) == FALSE))
   {
       // если цель не в состоянии разговора
       if(other.aivar[AIV_INVINCIBLE] == FALSE)
       {
           // НПС атакует цель (Примечание: функция AI_Attack работает только когда очередь AI состояний НПС пуста)
           AI_Attack(self);
       }
       else
       {
           // очистка очереди AI состояний НПС
           Npc_ClearAIQueue(self);
       };
       // запись НПС id последней цели
       self.aivar[AIV_LASTTARGET] = Hlp_GetInstanceID(other);
       return LOOP_CONTINUE;
   }
   else // иначе (если цель отсутствует или обездвижена)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // если цель существует и цель ГГ и ГГ обездвижен
       if(Hlp_IsValidNpc(other)) && (Npc_IsPlayer(other)) && (C_NpcIsDown(other))
       {
           // замена временного отношения НПС к ГГ на постоянное
           Npc_SetTempAttitude(self,Npc_GetPermAttitude(self,hero));
       };
       // если причина атаки НПС не убийство цели
       if(self.aivar[AIV_ATTACKREASON] != AR_KILL)
       {
           // НПС разрешено воспринимать все объекты
           Npc_PerceiveAll(self);
           // НПС ищет новую цель other
           Npc_GetNextTarget(self);
       };
       // если цель существует и она не обездвижена
       if(Hlp_IsValidNpc(other)) && (!C_NpcIsDown(other))
       // и (расстояние от НПС до цели < дистанции восприятия или цель ГГ)
       && ((Npc_GetDistToNpc(self,other) < PERC_DIST_INTERMEDIAT) || (Npc_IsPlayer(other)))
       // и расстояние между НПС и целью по высоте < дистанции восприятия по высоте
       && (Npc_GetHeightToNpc(self,other) < PERC_DIST_HEIGHT)
       // и цель не в разговоре
       && (other.aivar[AIV_INVINCIBLE] == FALSE)
       // и цель ГГ не является переодетым бандитом и НПС бандит
       && (!(C_PlayerIsFakeBandit(self,other) && (self.guild == GIL_BDT)))
       {
           // если гильдии НПС и цели враждебны
           if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_HOSTILE)
           {
               // причина атаки - враждебная гильдия
               self.aivar[AIV_ATTACKREASON] = AR_GuildEnemy;
               // если цель ГГ
               if(Npc_IsPlayer(other))
               {
                   // установить причину последнего нападения на ГГ
                   self.aivar[AIV_LastPlayerAR] = AR_GuildEnemy;
                   // параметр последнего сражения с ГГ - прервано
                   self.aivar[AIV_LastFightAgainstPlayer] = FIGHT_CANCEL;
                   // сброс комментария сражения
                   self.aivar[AIV_LastFightComment] = FALSE;
               };
           }
           // иначе, если сам НПС является врагом цели
           else if(Npc_GetAttitude(self,other) == ATT_HOSTILE)
           {
               // причина атаки = причину последнего нападения на ГГ
               self.aivar[AIV_ATTACKREASON] = self.aivar[AIV_LastPlayerAR];
           };
           return LOOP_CONTINUE;
       }
       else
       {
           // очистка очереди AI состояний НПС
           Npc_ClearAIQueue(self);
           // если последнее сражение с ГГ прервано и последняя цель не ГГ
           if(self.aivar[AIV_LastFightAgainstPlayer] == FIGHT_CANCEL) && (self.aivar[AIV_LASTTARGET] != Hlp_GetInstanceID(hero))
           {
               // комментировать сражение не нужно
               self.aivar[AIV_LastFightComment] = TRUE;
           };
           return LOOP_END;
       };
   };
};

//Функция завершения состояния атаки
func void ZS_Attack_End()
{
   // установить other на последнюю цель
   other = Hlp_GetNpc(self.aivar[AIV_LASTTARGET]);
   // усли преследование было прекращено
   if(self.aivar[AIV_PursuitEnd] == TRUE)
   {
       // если цель существует и цель ГГ и НПС не друг цели
       if(Hlp_IsValidNpc(other)) && (Npc_IsPlayer(other)) && (self.npctype != NPCTYPE_FRIEND)
       {
           // установить временное отношение НПС к ГГ на враждебное
           Npc_SetTempAttitude(self,ATT_HOSTILE);
       };
       // если НПС сражался на арене (состояние - в процессе сражения)
       if(self.aivar[AIV_ArenaFight] == AF_RUNNING)
       {
           // установить состояние после сражения
           self.aivar[AIV_ArenaFight] = AF_AFTER;
       };
   };
   // если преследования не было
   if(self.aivar[AIV_PursuitEnd] == FALSE)
   {
       // если уровень достигнутый ГГ в сражении > первоначального уровня
       if(B_GetCurrentAbsolutionLevel(self) > self.aivar[AIV_LastAbsolutionLevel])
       {
           // НПС говорит цели "Умный малый!" (SVM фраза)
           B_Say(self,other,"$WISEMOVE");
       }
       else
       {
           // обычная фраза завершения атаки
           B_Say_AttackEnd();
       };
   };
   // если цель убита ГГ и отношения гильдий НПС и ГГ не враждебные
   if(other.aivar[AIV_KilledByPlayer] == TRUE) && (Wld_GetGuildAttitude(self.guild,hero.guild) != ATT_HOSTILE)
   {
       // НПС становится другом ГГ
       B_SetAttitude(self,ATT_FRIENDLY);
   };
   // если цель находится без сознания и причиной атаки было убийство цели
   if(Npc_IsInState(other,ZS_UncoNPCious)) && (C_NpcHasAttackReasonToKill(self))
   {
       // НПС добивает цель
       B_FinishingMove(self,other);
   };
   // НПС прячет оружие
   AI_RemoveWeapon(self);
   // если цель обездвижена и НПС может обыскать цель и (цель не обыскивалась или должна быть обыскана)
   if(C_NpcIsDown(other)) && (C_WantToRansack(self)) && ((other.aivar[AIV_RANSACKED] == FALSE) || C_NpcRansacksAlways(self))
   // и расстояние между НПС и целью < дистанции восприятия
   && (Npc_GetDistToNpc(self,other) < PERC_DIST_INTERMEDIAT)
   {
       // установка флага, что цель обыскана
       other.aivar[AIV_RANSACKED] = TRUE;
       // цель человек
       if(other.guild < GIL_SEPERATOR_HUM)
       {
           // переход НПС в состояние грабежа цели
           AI_StartState(self,ZS_RansackBody,0,"");
           return;
       }
       else // (не человек)
       {
           // если НПС "Аллигатор Джек" и цель имеет "Сырое мясо"
           if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(AlligatorJack)) && (Npc_HasItems(other,ItFoMuttonRaw) > 0)
           {
               // Джек ест мясо
               AI_StartState(self,ZS_GetMeat,0,"");
               return;
           };
       };
   };
   // если жизнь НПС меньше половинной
   if(self.attribute[ATR_HITPOINTS] < (self.attribute[ATR_HITPOINTS_MAX]/2))
   {
       // НПС лечит себя
       AI_StartState(self,ZS_HealSelf,0,"");
       return;
   };
};

// *******************************************************
// Состояние защиты своего помещения
// *******************************************************
// Здесь: self - НПС хозяин помещения, other - "незванный гость" (ГГ)
// *******************************************************

// Функция обработки восприятия разговора (вызывается когда ГГ обращается к НПС хозяину)
func void B_ClearRoomTalk()
{
   // если НПС хочет напасть на "гостя"
   if(C_WantToAttackRoomIntruder(self))
   {
       // НПС предупреждает "гостя" (SVM фраза "Пошел вон!")
       B_Say(self,other,"$GETOUTOFHERE");
   }
   else
   {
       // НПС спрашивает "гостя" (SVM фраза "Что ты тут ищешь!?")
       B_Say(self,other,"$WHYAREYOUINHERE");
   };
};

// Функция обработки восприятия выхода ГГ из помещения
// Возвращает TRUE если ГГ покинул помещение, иначе - FALSE
func int B_ExitIfRoomLeft()
{
   var int portalguild;
   // получить номер гильдии, которой принадлежит помещение (-1 помещение никому не принадлежит)
   portalguild = Wld_GetPlayerPortalGuild();
   // если ГГ покинул помещение или помещение публичное
   if(!C_NpcIsBotheredByPlayerRoomGuild(self)) || (portalguild == GIL_PUBLIC)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       // НПС перестает смотреть на ГГ
       B_StopLookAt(self);
       // если НПС хочет напасть на "гостя"
       if(C_WantToAttackRoomIntruder(self))
       {
           // НПС говорит "гостю" (SVM фраза "Да, иди прочь!")
           B_Say(self,other,"$YESGOOUTOFHERE");
       }
       else
       {
           // НПС говорит "гостю" (SVM фраза "Умный малый!")
           B_Say(self,other,"$WISEMOVE");
       };
       // НПС продолжает выполнение распорядка дня
       AI_ContinueRoutine(self);
       return TRUE;
   };
   return FALSE;
};

// Инициализация состояния защиты помещения
func void ZS_ClearRoom()
{
   // установить минимальный набор восприятий НПС
   Perception_Set_Minimal();
   // заменить восприятие входа в помещение на восприятие выхода "гостя" из помещения
   Npc_PercEnable(self,PERC_ASSESSENTERROOM,B_ExitIfRoomLeft);
   // включить восприятие на перемещение МОВа
   Npc_PercEnable(self,PERC_MOVEMOB,B_MoveMob);
   // заменить восприятие разговора НПС
   Npc_PercEnable(self,PERC_ASSESSTALK,B_ClearRoomTalk);
   // НПС встает
   AI_StandUp(self);
   // НПС смотрит на "гостя"
   B_LookAtNpc(self,other);
   // НПС бежит к "гостю"
   AI_SetWalkmode(self,NPC_RUN);
   // если НПС находится вне помещения
   if(!Npc_IsInPlayersRoom(self))
   {
       // НПС бежит в точку, ближайшую к ГГ
       AI_GotoWP(self,Npc_GetNearestWP(other));
   };
   // установка признака входа в цикл
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла защиты помещения
func int ZS_ClearRoom_Loop()
{
   // если осуществен первый вход в цикл
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // НПС поворачивается к "гостю"
       B_TurnToNpc(self,other);
       // если НПС хочет напасть на "гостя"
       if(C_WantToAttackRoomIntruder(self))
       {
           // НПС выбирает соответствующее "гостю" оружие
           B_SelectWeapon(self,other);
           // НПС предупреждает "гостя" (SVM фраза "Пошел вон!")
           B_Say(self,other,"$GETOUTOFHERE");
       }
       else
       {
           // если НПС не является другом "гостя"
           if(Npc_GetAttitude(other,self) != ATT_FRIENDLY)
           {
               // НПС спрашивает "гостя" (SVM фраза "Что ты тут ищешь!?")
               B_Say(self,other,"$WHYAREYOUINHERE");
           };
       };
       // сброс времени нахождения НПС в этом состоянии
       Npc_SetStateTime(self, 0);
       // закрытие первого входа в цикл
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   // если ГГ покинул помещение
   if(B_ExitIfRoomLeft())
   {
       return LOOP_END;
   };
   // если НПС хочет напасть на "гостя"
   if(C_WantToAttackRoomIntruder(self))
   {
       // если с момента входа в помещение прошло > 5 сек
       if(Npc_GetStateTime(self) > 5)
       {
           // НПС атакует "гостя"
           B_Attack(self,other,AR_ClearRoom,0);
           return LOOP_END;
       };
   }
   else // НПС не хочет нападать (повторяется каждые две секунды)
   {
       // если с момента входа в помещение прошло >= 2 сек
       if(Npc_GetStateTime(self) >= 2)
       {
           // если НПС не видит "гостя" по прямой
           if(!Npc_CanSeeNpcFreeLOS(self,other))
           {
               // НПС бежит в точку, ближайшую к ГГ
               AI_GotoWP(self,Npc_GetNearestWP(other));
               // НПС поворачивается к "гостю"
               B_TurnToNpc(self,other);
           }
           // иначе, если НПС вообще не видит "гостя"
           else if(!Npc_CanSeeNpc(self,other))
           {
               // НПС поворачивается в направлении "гостя"
               B_TurnToNpc(self,other);
           };
           // сброс времени состояния
           Npc_SetStateTime(self,0);
       };
   };
   return LOOP_CONTINUE;
};

// Функция выхода из состояния защиты помещения
func void ZS_ClearRoom_End()
{
   // НПС перестает смотреть на "гостя"
   B_StopLookAt (self);
};

// ****************************************************
// Обработчик состояния когда НПС убегает от агрессора
// ----------------------------------------------------
// self - убегающий НПС, other - агрессор
// ****************************************************

// Функция инициализации состояния
func void ZS_Flee()
{
   // разрешить восприятие примененной магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // получить other - агрессор
   B_ValidateOther();
   // если загрузки игры не было
   if(self.aivar[AIV_LOADGAME] == FALSE)
   {
       // НПС кричит агрессору (SVM фраза "Я сматываюсь!")
       B_Say_Overlay(self,other,"$RUNAWAY");
   };
   // НПС прячет оружие
   AI_RemoveWeapon(self);
   // НПС переходит на бег
   AI_SetWalkmode(self,NPC_RUN);
   // проигрывается анимация убегающего человека
   Mdl_ApplyOverlayMds(self,"HUMANS_FLEE.MDS");
};

// Цикл состояния убегания
func int ZS_Flee_Loop()
{
   // получить для НПС цель (инициализировать other агрессором)
   Npc_GetTarget(self);
   // если расстояние между НПС и агрессором > дистанции преследования
   if(Npc_GetDistToNpc(self,other) > FIGHT_DIST_CANCEL)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
   // если агрессор обездвижен (мертв, без сознания и т.д.)
   if(C_NpcIsDown(other))
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
   // НПС продолжает убегать
   AI_Flee(self);
   return LOOP_CONTINUE;
};

// Функция завершения состояния убегания
func void ZS_Flee_End()
{
   // прекратить проигрывание анимации убегающего человека
   Mdl_RemoveOverlayMDS(self,"HUMANS_FLEE.MDS");
   // НПС останавливается
   AI_StandUp(self);
   // вызов функции завершения состояния лечения НПС
   AI_StartState(self,ZS_HealSelf,1,"");
   return;
};

// *********************************
// Обработчик состояния лечения НПС
// *********************************

// Инициализация состояния
func void ZS_HealSelf()
{
   // разрешить НПС минимальный набор восприятий
   Perception_Set_Minimal();
};

// Цикл состояния
func int ZS_HealSelf_Loop()
{
   // если текущая жизнь НПС равняется максимальной
   if(self.attribute[ATR_HITPOINTS] == self.attribute[ATR_HITPOINTS_MAX])
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
   // если НПС имеет "Лечебный эликсир"
   if(Npc_HasItems(self,ItPo_Health_03) > 0)
   {
       // НПС пьет "Лечебный эликсир"
       AI_UseItem(self,ItPo_Health_03);
       return LOOP_CONTINUE;
   }
   // если НПС имеет "Лечебный экстракт"
   else if(Npc_HasItems(self,ItPo_Health_02) > 0)
   {
       // НПС пьет "Лечебный экстракт"
       AI_UseItem(self,ItPo_Health_02);
       return LOOP_CONTINUE;
   }
   // если НРС имеет "Лечебную эссенцию"
   else if(Npc_HasItems(self,ItPo_Health_01) > 0)
   {
       // НПС пьет "Лечебную эссенцию"
       AI_UseItem(self,ItPo_Health_01);
       return LOOP_CONTINUE;
   }
   else //иначе
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
};

// Функция завершения состояния лечения
func void ZS_HealSelf_End()
{
};

// *******************************************
// Обработчик состояния наблюдения НПС за ГГ
// -------------------------------------------
// self - наблюдающий НПС, other - ГГ
// *******************************************

// Инициализация наблюдения
func void ZS_ObservePlayer()
{
   // установить НПС нормальный набор восприятий
   Perception_Set_Normal();
   // если НПС не сидит
   if(!C_BodystateContains(self,BS_SIT))
   {
       // НПС останавливается
       AI_StandUp(self);
       // НПС смотрит на ГГ
       B_LookAtNpc(self,other);
       // НПС поворачивается к ГГ
       B_TurnToNpc(self,other);
   }
   else // иначе (если НПС сидит)
   {
       // НПС смотрит на ГГ
       B_LookAtNpc(self,other);
   };
   // если в предудущем состоянии НПС спал
   if(Npc_WasInState(self,ZS_Sleep))
   {
       // НПС спрашивает ГГ (SVM фраза "Что случилось?")
       B_Say(self,other,"$YOUDISTURBEDMYSLUMBER");
   };
   // если НПС наблюдает за своим помещением и other есть ГГ
   if(self.aivar[AIV_SeenLeftRoom] == TRUE) && (Npc_IsPlayer(other))
   {
       // сброс наблюдения за своим помещением
       self.aivar[AIV_SeenLeftRoom] = FALSE;
       // если комментария входа в чужое помещение не было
       if(Player_LeftRoomComment == FALSE)
       {
           // НПС говорит ГГ (SVM фраза "Чего ты там ищешь?")
           B_Say(self,other,"$WHATDIDYOUDOINTHERE");
           // установка флага состоявшегося комментария
           Player_LeftRoomComment = TRUE;
       };
   };
   // установить время нахождения НПС в этом состоянии (1 или 2 сек)
   self.aivar[AIV_StateTime] = (Hlp_Random(2) + 1);
};

// Цикл состояния наблюдения
func int ZS_ObservePlayer_Loop ()
{
   // если ГГ крадется и комментария подкрадывания не было
   if(C_BodyStateContains(other,BS_SNEAK)) && (Player_SneakerComment == FALSE)
   {
       // установка флага состоявшегося комментария
       Player_SneakerComment = TRUE;
       // НПС показывает на ГГ
       AI_PointAtNpc(self,other);
       // НПС говорит ГГ (SVM фраза "Эй, ты! Что ты там шастаешь?")
       B_Say(self,other,"$WHATSTHISSUPPOSEDTOBE");
       // НПС перестает указывать на ГГ
       AI_StopPointAt(self);
       // НПС посылает ГГ предупреждение
       Npc_SendPassivePerc(self,PERC_ASSESSWARN,self,other);
   };
   // если время нахождения в состоянии > 1 или 2 сек
   if(Npc_GetStateTime(self) > self.aivar[AIV_StateTime])
   {
       // если НПС не сидит
       if(!C_BodystateContains(self,BS_SIT))
       {
           // НПС поворачивается к ГГ
           B_TurnToNpc(self,other);
       };
       // установить время нахождения НПС в этом состоянии (1 или 2 сек)
       self.aivar[AIV_StateTime] = (Hlp_Random(2) + 1);
       // сброс времени состояния
       Npc_SetStateTime(self,0);
   };
   // если расстояние между НПС и ГГ > дальности действия восприятия
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT)
   {
       // очистка очереди состояний НПС
       Npc_ClearAIQueue(self);
       return LOOP_END;
   }
   else // [Прим. else здесь лишнее, можно просто: return LOOP_CONTINUE;]
   {
       return LOOP_CONTINUE;
   };
};

// Выход из состояния наблюдения
func void ZS_ObservePlayer_End ()
{
   // НПС перестает смотреть на ГГ
   B_StopLookAt(self);
};

// *****************************************
// Обработчик состояния грабежа предметов с тел НПС
// -----------------------------------------
// self - грабитель, other - тело
// *****************************************

// Инициализация состояния
func void ZS_RansackBody()
{
   // установить грабителю нормальный набор восприятий
   Perception_Set_Normal();
   // грабитель встает
   AI_StandUp(self);
   // грабитель идет к телу
   AI_GotoNpc(self,other);
};

// Цикл грабежа
func int ZS_RansackBody_Loop()
{
   return LOOP_END;
};

// Завершающая фаза грабежа
func void ZS_RansackBody_End()
{
   // грабитель поворачивается к телу
   B_TurnToNpc(self,other);

   // включить анимацию грабежа
   AI_PlayAni(self,"T_PLUNDER");
   // если тело имеет "Святой молот" и грабитель "Гарвиг"
   if(Npc_HasItems(other,Holy_Hammer_MIS) > 0) && (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Garwig))
   {
       // создать в инвентаре грабителя "Святой молот"
       CreateInvItems(self,Holy_Hammer_MIS,1);
       // удалить из инвентаря тела "Святой молот"
       Npc_RemoveInvItems(other,Holy_Hammer_MIS,1);
   };
   // если тело имеет "Меч Рода" и грабитель "Род"
   if(Npc_HasItems(other,ItMw_2h_Rod) > 0) && (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Rod))
   {
       // переместить "Меч Рода" из инвентаря тела в инвентарь грабителя
       CreateInvItems(self,ItMw_2h_Rod,1);
       Npc_RemoveInvItems(other,ItMw_2h_Rod,1);
       // грабитель экипируется лучшим оружием ближнего радиуса поражения
       AI_EquipBestMeleeWeapon(self);
   };
   // если тело имеет "Ключ к хибаре капитана Грега" и грабитель "Фрэнсис"
   if(Npc_HasItems(other,ITKE_Greg_ADDON_MIS)) && (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Francis))
   {
       // передать предмет грабителю
       CreateInvItems(self,ITKE_Greg_ADDON_MIS,1);
       Npc_RemoveInvItems(other,ITKE_Greg_ADDON_MIS,1);
   };
   // если тело имеет "Золото"
   if(Npc_HasItems(other,ItMi_Gold) > 0)
   {
       var int x;
       // кол-во золота у тела
       x = Npc_HasItems(other,ItMi_Gold);
       // передать все золдото грабителю
       CreateInvItems(self,ItMi_Gold,x);
       Npc_RemoveInvItems(other,ItMi_Gold,x);
       // грабитель говорит телу (SVM фраза "Спасибо за золото, герой!")
       B_Say(self,other,"$ITOOKYOURGOLD");
   }
   else // иначе
   {
       // грабитель говорит телу (SVM фраза "Ты, жалкое ничтожество, разве у тебя нет золота!")
       B_Say(self,other,"$SHITNOGOLD");
   };
   // грабителю разрешается воспринимать все объекты в зоне действия восприятий
   Npc_PerceiveAll(self);
   // если грабитель находит оружие ближнего или дальнего радиуса поражения
   if(Wld_DetectItem(self,ITEM_KAT_NF) || Wld_DetectItem(self,ITEM_KAT_FF))
   {
       // если ссылка на предмет item существует
       if(Hlp_IsValidItem(item))
       {
           // если расстояние от грабителя до предмета < 5 метров
           if(Npc_GetDistToItem(self,item) < 500)
           {
               // грабитель берет предмет
               AI_TakeItem(self,item);
               // грабитель говорит сам себе (SVM фраза "Твое оружие я возьму с собой.")
               B_Say(self,self,"$ITAKEYOURWEAPON");
               // экипировка лучшим оружием
               AI_EquipBestMeleeWeapon(self);
               AI_EquipBestRangedWeapon(self);
           };
       };
   };
   // если жизнь грабителя < половинной
   if(self.attribute[ATR_HITPOINTS] < (self.attribute[ATR_HITPOINTS_MAX]/2))
   {
       // переход в состояние самолечения
       AI_StartState(self,ZS_HealSelf,0,"");
       return;
   };
};

// ***********************************************
// Обработчик состояния когда Джек забирает сырое мясо у жертвы
// -----------------------------------------------
// self - НПС Джек, other - тело
// ***********************************************

// Инициализация состояния
func void ZS_GetMeat()
{
   // разрешить минимальный набор восприятий
   Perception_Set_Minimal();
   // НПС встает
   AI_StandUp(self);
   // идет к телу
   AI_GotoNpc(self,other);
   // поворачивается к телу
   AI_TurnToNpc(self,other);
   // проигрывание анимации грабежа
   AI_PlayAni(self,"T_PLUNDER");
   var int x;
   // кол-во "Сырого мяса" у тела
   x = Npc_HasItems(other,ItFoMuttonRaw);
   // передать мясо от тела НПС
   CreateInvItems(self,ItFoMuttonRaw,x);
   Npc_RemoveInvItems(other,ItFoMuttonRaw,x);
   // если жизнь НПС < половинной
   if(self.attribute[ATR_HITPOINTS] < (self.attribute[ATR_HITPOINTS_MAX]/2))
   {
       // переход в состояние самолечения
       AI_StartState(self,ZS_HealSelf,0,"");
       return;
   };
};

// ************************************************************
// Обработчик состояния реакции НПС на повреждение
// ------------------------------------------------------------
// self - НПС, получивший повреждение, other - агрессор
// ************************************************************

// Инициализация состояния
func void ZS_ReactToDamage()
{
   // разрешить НПС нормальный набор восприятий
   Perception_Set_Normal();
   // НПС смотрит на агрессора
   B_LookAtNpc(self,other);
   // НПС выбирает оружие для поражения агрессора
   B_SelectWeapon(self,other);
   // НПС поворачивается к агрессору
   B_TurnToNpc(self,other);
   // НПС говорит агрессору (SVM фраза "Парень, никогда так больше не делай!")
   B_Say (self, other, "$WHATAREYOUDOING");
   // очистка счетчика секунд
   self.aivar[AIV_StateTime] = 0;
};

// Цикл состояния
func int ZS_ReactToDamage_Loop()
{
   // если прошла одна секунда
   if(Npc_GetStateTime(self) > self.aivar[AIV_StateTime])
   {
       // если НПС не видит агрессора
       if(!Npc_CanSeeNpc(self,other))
       {
           // НПС поворачивается к агрессору
           AI_TurnToNpc(self,other);
       };
       // счетчик секунд ++
       self.aivar[AIV_StateTime] = self.aivar[AIV_StateTime] + 1;
   };
   // если прошло более 10 секунд
   if(Npc_GetStateTime (self) > 10)
   {
       return LOOP_END;
   }
   else // [Примечание: else можно убрать, просто - return LOOP_CONTINUE;]
   {
       return LOOP_CONTINUE;
   };
};

// Завершения состояния
func void ZS_ReactToDamage_End()
{
   // НПС убирает оружие
   AI_RemoveWeapon(self);
   // перестает смотреть на агрессора
   B_StopLookAt(self);
};

// ********************************************
// Обработчик состояния реакции НПС на оружие
// --------------------------------------------
// self - реагирующий НПС, other - агрессор
// ********************************************

// Инициализация состояния
func void ZS_ReactToWeapon()
{
   // разрешить НПС минимальный набор восприятий
   Perception_Set_Minimal();
   // разрешить НПС восприятие сражения двух других НПС
   Npc_PercEnable(self,PERC_ASSESSFIGHTSOUND,B_AssessFightSound);
   // реакция НПС на врага агрессора
   if(B_AssessEnemy())
   {
       return;
   };
   // НПС встает
   AI_StandUp(self);
   // смотрит на агрессора
   B_LookAtNpc(self,other);
   // выбирает оружие
   B_SelectWeapon(self,other);
   // поворачивается к агрессору
   B_TurnToNpc(self,other);
   // если НПС выиграл последнее сражение с ГГ и была причина сражения с ГГ и агрессор ГГ
   if(self.aivar[AIV_LastFightAgainstPlayer] == FIGHT_WON) && (self.aivar[AIV_LastPlayerAR] != AR_NONE) && (Npc_IsPlayer(other))
   {
       // НПС говорит ГГ (SVM фраза "Хочешь еще получить?")
       B_Say(self,other,"$LOOKINGFORTROUBLEAGAIN");
   }
   // иначе, если комментария обнаженного оружия не было
   else if(Player_DrawWeaponComment == FALSE)
   {
       // если агрессор хочет применить магию
       if(Npc_IsInFightMode(other,FMODE_MAGIC))
       {
           // НПС говорит агрессору (SVM фраза "Перестань колдовать!")
           B_Say(self,other,"$STOPMAGIC");
       }
       else // иначе
       {
           // НПС говорит агрессору (SVM фраза "Убери оружие!")
           B_Say(self,other,"$WEAPONDOWN");
       };
       // установка флага состоявшегося комментария
       Player_DrawWeaponComment = TRUE;
   };
   // НПС посылает предупреждение агрессору
   Npc_SendPassivePerc(self,PERC_ASSESSWARN,self,other);
   // сброс флага второго предупреждения
   self.aivar[AIV_TAPOSITION] = FALSE;
   // сброс счетчика времени
   self.aivar[AIV_StateTime] = 0;
};

// Цикл состояния
func int ZS_ReactToWeapon_Loop()
{
   // если расстояние между НПС и агрессором > дистанции действия восприятия
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС убирает оружие
       AI_RemoveWeapon(self);
       // перестает смотреть на агрессора
       B_StopLookAt(self);
       return LOOP_END;
   };
   // если агрессор убрал оружие
   if(Npc_IsInFightMode(other,FMODE_NONE))
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС говорит агрессору (SVM фраза "Умный малый!")
       B_Say(self,other,"$WISEMOVE");
       // НПС убирает оружие
       AI_RemoveWeapon(self);
       // перестает смотреть на агрессора
       B_StopLookAt (self);
       return LOOP_END;
   };
   // если прошла одна секунда
   if(Npc_GetStateTime(self) > self.aivar[AIV_StateTime])
   {
       // если НПС не видит агрессора
       if(!Npc_CanSeeNpc(self,other))
       {
           // НПС поворачивается к агрессору
           B_TurnToNpc(self,other);
       };
       // счетчик времени ++
       self.aivar[AIV_StateTime] = self.aivar[AIV_StateTime] + 1;
   };
   // если второго предупреждения не было и время в состоянии > 5 сек
   if(self.aivar[AIV_TAPOSITION] == FALSE) && (Npc_GetStateTime(self) > 5)
   {
       // если агрессор хочет применить магию
       if(Npc_IsInFightMode(other,FMODE_MAGIC))
       {
           // НПС говорит агрессору (SVM фраза "Убери свою магию! Ты что, оглох!?")
           B_Say(self,other,"$ISAIDSTOPMAGIC");
       }
       else
       {
           // НПС говорит агрессору (SVM фраза "Да убери же чертово оружие!")
           B_Say(self,other,"$ISAIDWEAPONDOWN");
       };
       // установить флаг второго предупреждения
       self.aivar[AIV_TAPOSITION] = TRUE;
   };
   // если время в состоянии > 10 сек
   if(Npc_GetStateTime(self) > 10)
   {
       // НПС атакует агрессора
       B_Attack(self,other,AR_ReactToWeapon,0);
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_ReactToWeapon_End()
{
   // НПС перестает смотреть на агрессора
   B_StopLookAt(self);
   // НПС переходит в заключительную фазу состояния наблюдения за ГГ
   AI_StartState(self,ZS_ObservePlayer,1,"");
};

// *****************************************
// Обработчик состояния разговора между НПС
// -----------------------------------------
// self - НПС источник информации, other - слушатель (всегда ГГ)
// *****************************************

var int zsTalkBugfix;   // флаг выхода из разговора

// Инициализация состояния разговора
func void ZS_Talk()
{
   // если ГГ уже с кем-то разговаривает
   if(other.aivar[AIV_INVINCIBLE] == TRUE)
   {
       return;
   };
   // установка флагов начала разговора (для обоих участников)
   self.aivar[AIV_INVINCIBLE] = TRUE;
   other.aivar[AIV_INVINCIBLE] = TRUE;
   // если НПС человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // если НПС сидит
       if(C_BodyStateContains(self,BS_SIT))
       {
           var c_npc target;
           // инициализация глобальной переменной target ссылкой на НПС, на которого смотрит источник информации
           target = Npc_GetLookAtTarget(self);
           // если target не существует
           if(!Hlp_IsValidNpc(target))
           {
               // НПС смотрит на ГГ
               AI_LookAtNpc(self,other);
           };
       }
       else // иначе
       {
           // НПС смотрит на ГГ
           B_LookAtNpc(self,other);
       };
       // НПС прячет оружие
       AI_RemoveWeapon(self);
   };
   // если НПС не сидит
   if(!C_BodystateContains(self,BS_SIT))
   {
       // НПС поворачивается к ГГ
       B_TurnToNpc(self,other);
   };
   // если ГГ не сидит
   if(!C_BodystateContains(other,BS_SIT))
   {
       // ГГ поворачивается к НПС
       B_TurnToNpc(other,self);
       // если расстояние между ГГ и НПС < 80 см
       if(Npc_GetDistToNpc(other,self) < 80)
       {
           // ГГ отспупает назад
           AI_Dodge(other);
       };
   };
   // если НПС человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // если НПС сердит на ГГ или НПС враг ГГ
       if(Npc_GetAttitude(self,other) == ATT_ANGRY) || (Npc_GetAttitude(self,other) == ATT_HOSTILE)
       {
           // если ГГ не переодет в бандита или НПС не бандит
           if(!C_PlayerIsFakeBandit(self,other)) || (self.guild != GIL_BDT)
           {
               // включить "злую" анимацию лица НПС
               Mdl_StartFaceAni(self,"S_ANGRY",1,-1);
           };
       };
       // если НПС принадлежит к окружающему народу
       if(self.npctype == NPCTYPE_AMBIENT)         // общему
       || (self.npctype == NPCTYPE_OCAMBIENT)      // или из Миненталя
       || (self.npctype == NPCTYPE_BL_AMBIENT)     // или из Лагеря бандитов
       || (self.npctype == NPCTYPE_TAL_AMBIENT)    // или из Долины бандитов
       {
           // инициализация диалога НПС об окружающей информации (сплетни)
           B_AssignAmbientInfos(self);
           // если НПС принадлежит городу
           if(C_NpcBelongsToCity(self))
           {
               // инициализация диалога НПС о городской жизни
               B_AssignCityGuide(self);
           };
       };
       // если НПС является членом партии ГГ и...
       if(self.aivar[AIV_PARTYMEMBER] == TRUE)
       && (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(Biff))         // НПС не Бифф
       && (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(Biff_NW))      // и НПС не Бифф в Новом мире
       && (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(Biff_DI))      // и НПС не Бифф на Острове драконов
       && (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(Pardos))       // и НПС не Пардос
       && (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(Pardos_NW))    // и НПС не Пардос в Новом мире
       {
           // ГГ дает НПС напиток лечения
           B_Addon_GivePotion(self);
       };
       // если НПС имеет дело с "крутым парнем" и НПС не имеет новостей о "крутом парне"
       if(C_NpcIsToughGuy(self)) && (self.aivar[AIV_ToughGuyNewsOverride] == FALSE)
       {
           // инициализация диалога НПС о "крутом парне"
           B_AssignToughGuyNEWS(self);
       };
       // если НПС имеет новости об окружающей жизни
       if(C_NpcHasAmbientNews(self))
       {
           // инициализация диалога НПС об окружающих новостях
           B_AssignAmbientNEWS(self);
       };
   };
   // если НПС дракон
   if (self.guild == GIL_DRAGON)
   {
       // проиграть анимацию дракона T_STAND_2_TALK
       AI_PlayAni(self,"T_STAND_2_TALK");
   };
   // начать выбранный диалог
   AI_ProcessInfos(self);
   // сброс флага выхода из разговора
   zsTalkBugfix = FALSE;
};

// Цикл состояния разговора
func int ZS_Talk_Loop ()
{
   // если диалог закончен и флаг выхода установлен
   if(InfoManager_HasFinished()) && (zsTalkBugfix == TRUE)
   {
       // сброс флагов разговора для обоих участников
       self.aivar[AIV_INVINCIBLE] = FALSE;
       other.aivar[AIV_INVINCIBLE] = FALSE;
       // сброс флага важной информации
       self.aivar[AIV_NpcStartedTalk] = FALSE;
       // установка флага, что НПС говорил с ГГ
       self.aivar[AIV_TalkedToPlayer] = TRUE;
       // если НПС человек
       if(self.guild < GIL_SEPERATOR_HUM)
       {
           // НПС перестает смотреть на ГГ
           B_StopLookAt(self);
           // сменить выражение лица НПС на обычное
           Mdl_StartFaceAni(self,"S_NEUTRAL",1,-1);
       };
       // если НПС дракон
       if(self.guild == GIL_DRAGON)
       {
           // запустить обычную анимацию стоящего дракона
           AI_PlayAni(self,"T_TALK_2_STAND");
       };
       return LOOP_END;
   }
   else // иначе
   {
       // установить флаг выхода из разговора (нельзя завершить цикл разговора при первом вхождении)
       zsTalkBugfix = TRUE;
       return LOOP_CONTINUE;
   };
};

// Окончание состояния разговора
func void ZS_Talk_End()
{
   // отказ ГГ от других диалогов на 20 сек
   Npc_SetRefuseTalk(other,20);
   // когда НПС находится в своем помещении или (помещение публично и НПС не друг ГГ
   if(C_NpcIsBotheredByPlayerRoomGuild(self)) || ((Wld_GetPlayerPortalGuild() == GIL_PUBLIC) && (Npc_GetAttitude(self,other) != ATT_FRIENDLY))
   {
       // НПС переходит в состояние наблюдения за ГГ
       AI_StartState(self,ZS_ObservePlayer,0,"");
   }
   else    // [лишнее, можно удалить]
   {

   };
};

// ***********************************************************
// Обработчик состояния наблюдения за сражением
// -----------------------------------------------------------
// self - НПС наблюдатель, other - агрессор, victim - жертва
// ***********************************************************

// Функция реакции НПС наблюдателя когда другой НПС получает повреждение
func void B_CheerFight()
{
   // если время нахождения наблюдателя в этом состоянии <= 2 сек
   if(Npc_GetStateTime(self) <= 2)
   {
       return;
   };
   // если агрессор монстр или жертва монстр
   if((other.guild > GIL_SEPERATOR_HUM) || (victim.guild > GIL_SEPERATOR_HUM))
   {
       return;
   };
   // сброс времени нахождения в состоянии
   Npc_SetStateTime(self,0);
   // если наблюдатель имеет дело с "крутым парнем"
   if(C_NpcIsToughGuy(self))
   {
       var int zufall;
       // случайное число
       zufall = Hlp_Random(3);
       // если наблюдатель друг жертвы и наблюдатель не друг агрессору
       if(Npc_GetAttitude(self,victim) == ATT_FRIENDLY) && (Npc_GetAttitude(self,other) != ATT_FRIENDLY)
       {
           // наблюдатель говорит сам себе (SVM фраза)
           if(zufall == 0) { B_Say_Overlay(self,self,"$OOH01"); }; // "Смотри, не делай так!"
           if(zufall == 1) { B_Say_Overlay(self,self,"$OOH02"); }; // "Вот тогда ты еще успеешь это сделать!"
           if(zufall == 2) { B_Say_Overlay(self,self,"$OOH03"); }; // "Ох! Это же больно!"
           // проиграть анимацию
           AI_PlayAni(self,"T_WATCHFIGHT_OHNO");
       }
       else // иначе
       {
           // наблюдатель говорит сам себе (SVM фраза)
           if(zufall == 0) { B_Say_Overlay(self,self,"$CHEERFRIEND01"); }; // "Да, это хорошо!"
           if(zufall == 1) { B_Say_Overlay(self,self,"$CHEERFRIEND02"); }; // "Ну, сделай уже!"
           if(zufall == 2) { B_Say_Overlay(self,self,"$CHEERFRIEND03"); }; // "Все просто здорово!"
           // проиграть анимацию
           AI_PlayAni(self,"T_WATCHFIGHT_YEAH");
       };
   };
};

// Функция реакции НПС наблюдателя когда другой НПС падает без сознания
func void B_AssessDefeat()
{
   // очистка очереди AI состояний наблюдателя
   Npc_ClearAIQueue(self);
   // если наблюдатель имеет дело с "крутым парнем" или (агрессор ГГ и наблюдатель друг ГГ)
   if(C_NpcIsToughGuy(self)) || (Npc_IsPlayer(other) && (self.npctype == NPCTYPE_FRIEND))
   {
       // если наблюдатель друг ГГ
       if(Npc_GetAttitude(self,other) == ATT_FRIENDLY)
       {
           // наблюдатель говорит ГГ (SVM фраза "Ну и показал же ты ему!")
           B_Say(self,other,"$GOODVICTORY");
       }
       else // иначе
       {
           // наблюдатель говорит ГГ (SVM фраза "Неплохо...")
           B_Say(self,other,"$NOTBAD");
       };
   }
   else // иначе
   {
       // если у жертвы не было причины сражаться
       if(victim.aivar[AIV_ATTACKREASON] != AR_NONE)
       {
           // наблюдатель говорит агрессору (SVM фраза "Боже мой! Какая жестокость...")
           B_Say(self,other,"$OHMYGODHESDOWN");
           // наблюдатель запоминает новость, что агрессор сражался с НПС жертвой
           B_MemorizePlayerCrime(self,other,CRIME_ATTACK);
       }
       else // иначе
       {
           // наблюдатель говорит агрессору (SVM фраза "Неплохо...")
           B_Say(self,other,"$NOTBAD");
       };
   };
};

// Инициализация состояния наблюдения за сражением
func void ZS_WatchFight ()
{
   // разрешить наблюдателю локальное восприятие бессознательного состояния НПС
   Npc_PercEnable(self,PERC_ASSESSDEFEAT,B_AssessDefeat);
   // разрешить наблюдателю локальное восприятие если НПС получил повреждение
   Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,B_CheerFight);
   // разрешить минимальный набор восприятий
   Perception_Set_Minimal();
   // наблюдатель встает
   AI_StandUp(self);
   // поворачивается к жертве
   B_TurnToNpc(self,victim);
   // прячет оружие
   AI_RemoveWeapon(self);
   // если (расстояние между наблюдателем и агрессором или наблюдателем и жертвой < дистанции действия восприятия)
   if((Npc_GetDistToNpc(self,other) < PERC_DIST_INTERMEDIAT) || (Npc_GetDistToNpc(self,victim) < PERC_DIST_INTERMEDIAT))
   // и агрессор и жертва не без сознания
   && (!Npc_IsInState(other,ZS_UncoNPCious)) && (!Npc_IsInState(victim,ZS_UncoNPCious))
   // и агрессор и жертва люди
   && ((other.guild < GIL_SEPERATOR_HUM) && (victim.guild < GIL_SEPERATOR_HUM))
   {
       // если наблюдатель имеет дело с "крутым парнем""
       if(C_NpcIsToughGuy(self))
       {
           // наблюдатель говорит агрессору (SVM фраза "Ух ты, это же битва!")
           B_Say(self,other,"$THERESAFIGHT");
       }
       else // иначе
       {
           // наблюдатель говорит агрессору (SVM фраза "Боже мой, вот это битва!")
           B_Say(self,other,"$OHMYGODITSAFIGHT");
       };
   };
   // сброс флага выхода из цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
   // очистить счетчик времени
   self.aivar[AIV_StateTime] = 0;
};

// Цикл состояния наблюдения за сражением
func int ZS_WatchFight_Loop()
{
   // если расстояние между наблюдателем и агрессором и наблюдателем и жертвой > дистанции наблюдения сражения
   if(Npc_GetDistToNpc(self,other) > WATCHFIGHT_DIST_MAX) && (Npc_GetDistToNpc(self,victim) > WATCHFIGHT_DIST_MAX)
   {
       // очистка очереди AI состояний наблюдателя
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
   // если (агрессор не атакует или не реагирует на повреждение)
   if(!(Npc_IsInState(other,ZS_Attack) || Npc_IsInState(other,ZS_ReactToDamage)))
   // и (жертва не атакует или не реагирует на повреждение)
   && (!(Npc_IsInState(victim,ZS_Attack) || Npc_IsInState(victim,ZS_ReactToDamage)))
   // и время в состоянии > 0
   && (Npc_GetStateTime(self) > 0)
   {
       // если агрессор или жертва без сознания
       if(Npc_IsInState(other,ZS_UncoNPCious)) || (Npc_IsInState(victim,ZS_UncoNPCious))
       // или агрессор или жертва мертвы
       || (Npc_IsInState(other,ZS_Dead)) || (Npc_IsInState(victim,ZS_Dead))
       {
           // если флаг выхода из цикла сброшен
           if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
           {
               // установить флаг
               self.aivar[AIV_TAPOSITION] = ISINPOS;
               return LOOP_CONTINUE;
           }
           else // иначе, флаг выхода из цикла установлен
           {
               // очистка очереди AI состояний наблюдателя
               Npc_ClearAIQueue(self);
               // выход из цикла выполняется после второго прохода, если кто-либо из сражающихся мертв или без сознания (чтобы сработало соответствующее восприятие)
               return LOOP_END;
           };
       }
       else
       {
           // очистка очереди AI состояний наблюдателя
           Npc_ClearAIQueue(self);
           return LOOP_END;
       };
   };
   // если наблюдатель имеет дело с "крутым парнем"
   if(C_NpcIsToughGuy(self))
   {
       // анимация удалена!!!
       //AI_PlayAni(self,"T_STAND_2_WATCHFIGHT");
   };
   // если расстояние между наблюдателем и агрессором или наблюдателем и жертвой <= мин. дистанции наблюдения сражения
   if(Npc_GetDistToNpc(self,other)  <= WATCHFIGHT_DIST_MIN) || (Npc_GetDistToNpc(self,victim) <= WATCHFIGHT_DIST_MIN)
   {
       // очистка очереди AI состояний наблюдателя
       Npc_ClearAIQueue(self);
       // если расстояние между наблюдателем и агрессором <= расстояния между наблюдателем и жертвой
       if(Npc_GetDistToNpc(self,other) <= Npc_GetDistToNpc(self,victim))
       {
           // наблюдатель поворачивается к жертве
           B_TurnToNpc(self,victim);
       }
       else // иначе
       {
           // наблюдатель поворачивается к агрессору
           B_TurnToNpc(self,other);
       };
       // наблюдатель делает шаг назад
       AI_Dodge(self);
   }
   else // иначе (расстояние нормальное для наблюдения)
   {
       // если прошла секунда
       if(Npc_GetStateTime(self) != self.aivar[AIV_StateTime])
       {
           // если если расстояние между наблюдателем и агрессором <= расстояния между наблюдателем и жертвой
           if(Npc_GetDistToNpc(self,other) <= Npc_GetDistToNpc(self,victim))
           {
               // наблюдатель поворачивается к агрессору
               B_TurnToNpc(self,other);
           }
           else // иначе
           {
               // наблюдатель поворачивается к жертве
               B_TurnToNpc(self,victim);
           };
           // запись в счетчик текущего времени цикла
           self.aivar[AIV_StateTime] = Npc_GetStateTime(self);
       };
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_WatchFight_End()
{
   // анимация удалена!!!
   //AI_PlayAni(self,"T_WATCHFIGHT_2_STAND");
};

// *****************************************************
// Обработчик состояния когда НПС находится без сознания
// Примечание: НПС в это состояние переводит ядро системы, когда у НПС кончается жизнь, а не скрипты!
// -----------------------------------------------------
// self - НПС без сознания, other - агрессор
// *****************************************************

// Инициализация состояния
func void ZS_UncoNPCious()
{
   // разрешить НПС воспринимать магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // если НПС плыл или нырял
   if(C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // очистка всех восприятий
       B_ClearPerceptions(self);
       // переход НПС в состояние трупа (смерть)
       AI_StartState(self,ZS_Dead,0,"");
       return;
   };
   // сброс статуса предупреждения охраной
   self.aivar[AIV_Guardpassage_Status] = GP_NONE;
   // сброс счетчика отказа от диалога
   Npc_SetRefuseTalk(self,0);
   // сброс временного отношения НПС к ГГ (устанавливается равным постоянному отношению)
   Npc_SetTempAttitude(self,Npc_GetPermAttitude(self,hero));
   // НПС перестает смотреть на кого-либо
   B_StopLookAt(self);
   // НПС прекращает на что-либо указывать
   AI_StopPointAt(self);
   // если НПС человек и агрессор ГГ
   if(self.guild < GIL_SEPERATOR_HUM) && (Npc_IsPlayer(other))
   {
       // установить флаг, что НПС побежден ГГ
       self.aivar[AIV_DefeatedByPlayer] = TRUE;
       // установить, что НПС проиграл сражение с ГГ
       self.aivar[AIV_LastFightAgainstPlayer] = FIGHT_LOST;
       // если причины у НПС нападать на ГГ не было и ГГ этого НПС из наемников еще не бил и НПС наемник
       if(self.aivar[AIV_LastPlayerAR] == AR_NONE) && (self.aivar[AIV_DuelLost] == FALSE) && (self.guild == GIL_SLD)
       {
           // увеличить счетчик для задания Торлофа (побить > 3 наемников)
           Sld_Duelle_gewonnen = Sld_Duelle_gewonnen + 1;
           // установить флаг, что наемник побит ГГ
           self.aivar[AIV_DuelLost] = TRUE;
       };
       //  если НПС сражался на арене с Альриком
       if(self.aivar[AIV_ArenaFight] == AF_RUNNING)
       {
           // запись следующей фазы сражения на арене НПС
           self.aivar[AIV_ArenaFight] = AF_AFTER;
       };
   };
   // если НПС без сознания ГГ
   if(Npc_IsPlayer(self))
   {
       // установить, что агрессор выиграл сражение с ГГ
       other.aivar[AIV_LastFightAgainstPlayer] = FIGHT_WON;
       // если ГГ сражался на арене с Альриком
       if(other.aivar[AIV_ArenaFight] == AF_RUNNING)
       {
           // запись следующей фазы сражения на арене Альрику
           other.aivar[AIV_ArenaFight] = AF_AFTER;
       };
   };
   // удаление продаваемых предметов у торговцев
   B_GiveTradeInv(self);
   // удаление рун у НПС
   B_ClearRuneInv(self);
   // если (агрессор ГГ или агрессор член партии ГГ) и начисления экспы ГГ за победу над НПС не было
   if(Npc_IsPlayer(other) || (other.aivar[AIV_PARTYMEMBER] == TRUE)) && (self.aivar[AIV_VictoryXPGiven] == FALSE)
   {
       // начисление ГГ экспы (уровень НПС * 10)
       B_GivePlayerXP(self.level * XP_PER_VICTORY);
       // установить НПС флаг начисления экспы
       self.aivar[AIV_VictoryXPGiven] = TRUE;
   };
   // убрать оружие экипированное НПС
   AI_UnequipWeapons(self);
   // если НПС "Дар" и агрессор "Сайфер"
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Dar)) && (Hlp_GetInstanceID(other) == Hlp_GetInstanceID(Cipher))
   {
       // установка флага для квеста с пакетом болотника
       Dar_LostAgainstCipher = TRUE;
   };
};

// Цикл состояния
func int ZS_UncoNPCious_Loop()
{
   // если время цикла < времени нахождения в бессознательном состоянии
   if(Npc_GetStateTime(self) < HAI_TIME_UNCONPCIOUS)
   {
       return LOOP_CONTINUE;
   }
   else // иначе
   {
       return LOOP_END;
   };
};

// Завершение состояния
func void ZS_UncoNPCious_End()
{
   // сброс флага, что тело обыскивалось
   self.aivar[AIV_RANSACKED] = FALSE;
   // НПС встает
   AI_StandUp(self);
   // если НПС ГГ
   if(Npc_IsPlayer(self))
   {
       return;
   };
   // если НПС "Равен"
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Raven))
   {
       // установка флага, что НПС всегда использует магию
       self.aivar[AIV_MagicUser] = MAGIC_ALWAYS;
       // Равен атакует ГГ
       B_Attack(self,hero,AR_GuildEnemy,0);
       return;
   };
   // если НПС видит агрессора по прямой и расстояние между НПС и агрессором < дистанции действия восприятия
   if(Npc_CanSeeNpcFreeLOS(self,other)) && (Npc_GetDistToNpc(self,other) < PERC_DIST_INTERMEDIAT)
   {
       // НПС поворачивается к агрессору
       B_TurnToNpc (self,other);
       // если НПС имел дело с "крутым парнем" и НПС не враг агрессору и НПС не друг ГГ
       if(C_NpcIsToughGuy(self)) && (Npc_GetPermAttitude(self,other) != ATT_HOSTILE) && (self.npctype != NPCTYPE_FRIEND)
       {
           // НПС говорит ГГ (SVM фраза "В следующий раз мы это увидим...")
           B_Say(self,other,"$NEXTTIMEYOUREINFORIT");
       }
       else // иначе
       {
           // НПС говорит ГГ (SVM фраза "О, мой череп...")
           B_Say(self,other,"$OHMYHEAD");
       };
   };
   // разрешить НПС воспринимать все объекты в зоне действия восприятия
   Npc_PerceiveAll(self);
   // если НПС находит оружие ближнего или дальнего радиуса поражения
   if(Wld_DetectItem(self,ITEM_KAT_NF)) || (Wld_DetectItem(self,ITEM_KAT_FF))
   {
       // если ссылка на предмет item существует
       if(Hlp_IsValidItem(item))
       {
           // если расстояние от НПС до предмета < 5 метров
           if(Npc_GetDistToItem(self,item) <= 500)
           {
               // НПС берет предмет
               AI_TakeItem(self,item);
           };
       };
   };
   // экипировка лучшим оружием
   AI_EquipBestMeleeWeapon(self);
   AI_EquipBestRangedWeapon(self);
   // переход НПС в состояние самолечения
   AI_StartState(self,ZS_HealSelf,0,"");
   return;
};

// *****************************************
// Обработчик состояния смерти НПС
// Примечание: НПС в это состояние переводит также и ядро системы, когда НПС добивают!
// -----------------------------------------
// self - мертвый НПС, other - убийца
// *****************************************

// Инициализация состояния смерти
func void ZS_Dead()
{
   // сброс флага, что тело обыскивалось
   self.aivar[AIV_RANSACKED] = FALSE;
   // сброс флага, что НПС член партии
   self.aivar[AIV_PARTYMEMBER] = FALSE;
   // НПС перестает смотреть на кого-либо
   B_StopLookAt(self);
   // НПС прекращает на что-либо указывать
   AI_StopPointAt(self);
   // если (убийца ГГ или убийца член партии ГГ) и начисления экспы ГГ за убийство НПС не было
   if(Npc_IsPlayer(other) || (other.aivar[AIV_PARTYMEMBER] == TRUE)) && (self.aivar[AIV_VictoryXPGiven] == FALSE)
   {
       // начисление ГГ экспы (уровень НПС * 10)
       B_GivePlayerXP (self.level * XP_PER_VICTORY);
       // установить НПС флаг начисления экспы
       self.aivar[AIV_VictoryXPGiven] = TRUE;
   };
   // если убит "Расчленитель" в каньоне
   if(C_IAmCanyonRazor(self) == TRUE)
   {
       // счетчик убитых расчленителей ++
       CanyonRazorBodyCount = CanyonRazorBodyCount + 1;
       // если квест Грега об очистке каньона в стадии выполнения
       if(MIS_Addon_Greg_ClearCanyon == LOG_RUNNING)
       {
           // выдача на экран информации об убитых расчленителях
           B_CountCanyonRazor();
       };
   };
   // если убита "Болотная вонючка"
   if(self.aivar[AIV_MM_REAL_ID] == ID_SWAMPDRONE)
   {
       // если расстояние между НПС и убийцей < 3 метров
       if(Npc_GetDistToNpc(self,other) < 300)
       {
           // жизнь убийцы уменьшается на 50 пунктов
           other.attribute[ATR_HITPOINTS] -= 50;
       };
   };
   // если Диего убит в Рудниковой долине
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(DiegoOW))
   {
       // установить флаг "воскрешения" Диего в Хоринисе
       Diego_IsDead = TRUE;
   };
   // если убийца ГГ
   if(Npc_IsPlayer(other))
   {
       // установить флаг, что НПС убит ГГ
       self.aivar[AIV_KilledByPlayer] = TRUE;
       // если этого НПС не разрешалось убивать
       if(C_DropUncoNPCious())
       {
           // счетчик невинно убитых ++
           MadKillerCount = (MadKillerCount + 1);
       };
       // если убит "Полевой жук" и квест Фестера в стадии выполнения
       if(self.guild == GIL_GIANT_BUG) && (MIS_Fester_KillBugs == LOG_RUNNING)
       {
           // увеличить счетчик убитых Полевых жуков
           Festers_Giant_Bug_Killed = Festers_Giant_Bug_Killed + 1;
       };
       // если убита "Болотная крыса" и квест охоты с Аллигатором Джеком в стадии выполнения
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Swamprat)) && (MIS_KrokoJagd == LOG_Running)
       {
           // увеличить счетчик убитых Болотных крыс
           AlligatorJack_KrokosKilled = AlligatorJack_KrokosKilled + 1;
       };
       // если убит "Рамон"
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Ramon))
       {
           // установка флага, что ГГ может разговаривать с бандитами
           Player_HasTalkedToBanditCamp = TRUE;
       };
       // если убит "Франко"
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Franco))
       {
           // если квест "Охота на болотожоров с Логаном" в стадии выполнения
           if(MIS_HlpLogan == LOG_RUNNING)
           {
               // квест завершается
               MIS_HlpLogan = LOG_OBSOLETE;
           };
           // если квест "Найти Эдгора с каменной плиткой" в стадии выполнения
           if(MIS_HlpEdgor == LOG_RUNNING)
           {
               // квест завершается
               MIS_HlpEdgor = LOG_OBSOLETE;
           };
       };
       // если убит "Фортуно"
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Fortuno))
       {
           // квест по "Поиску зеленого послушника" завершается
           Log_SetTopicStatus(Topic_Addon_Fortuno,LOG_OBSOLETE);
       };
   };
   // если убит "Ползун" в пещере (где должен появиться Блудвин)
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Goldminecrawler))
   {
       // если убито более 8 ползунов и Блудвина нет
       if(Minecrawler_Killed >= 9) && (Bloodwyn_Spawn == FALSE)
       {
           // Блудвин появляется в точке ADW_MINE_TO_MC_03
           AI_Teleport(Bloodwyn,"ADW_MINE_TO_MC_03");
           // установить Блудвину распорядок дня
           B_StartOtherRoutine(Bloodwyn,"MINE");
           // начислить ГГ 500 экспы
           B_GivePlayerXP(XP_Addon_Bloodywyn);
           // установить флаг появления Блудвина
           Bloodwyn_Spawn = TRUE;
       }
       else // иначе
       {
           // счетчик убитых ползунов ++
           Minecrawler_Killed = (Minecrawler_Killed + 1);
       };
   };
   // удаление продаваемых предметов у торговцев
   B_GiveTradeInv(self);
   // создать в инвентаре монстров добываемые предметы (в зависимости от способностей ГГ)
   B_GiveDeathInv(self);
   // удаление рун у НПС
   B_ClearRuneInv(self);
   // оставить счетчики криминала ГГ по локациям в старом состоянии
   B_DeletePetzCrime(self);
   // сброс флага криминала у НПС
   self.aivar[AIV_NpcSawPlayerCommit] = CRIME_NONE;
   // убрать оружие
   AI_UnequipWeapons(self);
   // сброс флага показа видео
   self.aivar[AIV_TAPOSITION] = FALSE;
};

// Цикл смерти (выхода из него нет)
func int ZS_Dead_loop()
{
   // если показа видео не было
   if(self.aivar[AIV_TAPOSITION] == FALSE)
   {
       // показать видео соответствующее смерти НПС
       B_DragonKillCounter(self);
       // установить флаг показа видео
       self.aivar[AIV_TAPOSITION] = TRUE;
   };
   return LOOP_CONTINUE;
};


9. Функции состояний монстров.

Все обработчики основных состояний монстров находятся в директории ..\AI\Monster\ZS_Monster\
Имена файлов соответствуют именам обработчиков.
// **************************************
// Обработчик состояния атаки
// --------------------------------------
// self - НПС агрессор, other - НПС цель
// **************************************

// Функция обработки вспомогательного восприятия PERC_ASSESSSURPRISE
// Вызывается ядром системы когда ГГ превращается в человека из животного
func void B_MM_AssessSurprise()
{
   // целью агрессора становится ГГ
   Npc_SetTarget(self,other);
};

// Мнициализация состояния атаки
func void ZS_MM_Attack()
{
   // установка времени реакции на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // разрешить восприятие мертвого тела
   Npc_PercEnable(self,PERC_ASSESSBODY,B_MM_AssessBody);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_MM_AssessDamage);
   // разрешить восприятие предупреждения
   Npc_PercEnable(self,PERC_ASSESSWARN,B_MM_AssessWarn);
   // разрешить вспомогательное восприятие превращения
   Npc_PercEnable(self,PERC_ASSESSSURPRISE,B_MM_AssessSurprise);
   // инициализировать переменную other (последняя цель)
   B_ValidateOther();
   // если агрессор овца
   if(self.guild == GIL_SHEEP)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // очистка восприятий
       B_ClearPerceptions(self);
       // установка цели
       Npc_SetTarget(self,other);
       // НПС убегает от цели
       AI_StartState(self,ZS_MM_Flee,0,"");
       return;
   };
   // НПС встает
   AI_StandUp(self);
   // включается режим передвижения - бег
   AI_SetWalkmode(self,NPC_RUN);
   // агрессору посылается предупреждение от цели
   Npc_SendPassivePerc(self,PERC_ASSESSWARN,other,self);
   // сброс флага окончания преследования
   self.aivar[AIV_PursuitEnd] = FALSE;
   // сброс счетчика времени нахождения в состоянии
   self.aivar[AIV_StateTime] = 0;
   // сброс флага первого удара
   self.aivar[AIV_HitByOtherNpc] = 0;
   // сброс выбранного заклинания
   self.aivar[AIV_SelectSpell] = 0;
   // сброс счетчика регенерации
   self.aivar[AIV_TAPOSITION] = 0;
};

// Цикл состояния атаки
func int ZS_MM_Attack_Loop()
{
   // повторно инициализировать other целью
   Npc_GetTarget(self);
   // если НПС дракон
   if(self.guild == GIL_DRAGON)
   {
       // счетчик регенерации ++
       self.aivar[AIV_TAPOSITION] += 1;
       // если жизнь НПС < максимальной и счетчик регенерации >= 2 cек
       if (self.attribute[ATR_HITPOINTS] < self.attribute[ATR_HITPOINTS_MAX]) && (self.aivar[AIV_TAPOSITION] >= 2)
       {
           // жизнь ++
           self.attribute[ATR_HITPOINTS] += 1;
           // сброс счетчика
           self.aivar[AIV_TAPOSITION] = 0;
       };
   };
   // если Равен убит и агрессор Каменный сторож
   if(RavenIsDead == TRUE) && (self.guild == GIL_STONEGUARDIAN)
   {
       // агрессор умирает
       B_KillNpc(self);
   };
   // если текущий уровень - Миненталь
   if(CurrentLevel == OLDWORLD_ZEN)
   {
       // если расстояние от агрессора до точки OC_RAMP_07 <= 5 метров
       if(Npc_GetDistToWP(self,"OC_RAMP_07") <= 500)
       {
           // очистка очереди AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает
           AI_StandUp(self);
           // проигрывается анимация T_WARN
           AI_PlayAni(self,"T_WARN");
           // установка флага окончания преследования
           self.aivar[AIV_PursuitEnd] = TRUE;
           return LOOP_END;
       };
   };
   // если расстояние между НПС и целью > дистанции сражения
   if(Npc_GetDistToNpc(self,other) > FIGHT_DIST_CANCEL)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       // установка флага окончания преследования
       self.aivar[AIV_PursuitEnd] = TRUE;
       return LOOP_END;
   };
   // если время преследования превышает допустимое и НПС преследует цель
   if(Npc_GetStateTime(self) > self.aivar[AIV_MM_FollowTime]) && (self.aivar[AIV_PursuitEnd] == FALSE)
   {
       Npc_ClearAIQueue(self);
       AI_StandUp(self);
       // НПС закончил преследование
       self.aivar[AIV_PursuitEnd] = TRUE;
       // получить дистанцию до цели
       self.aivar[AIV_Dist] = Npc_GetDistToNpc(self,other);
       // обновить время состояния НПС
       self.aivar[AIV_StateTime] = Npc_GetStateTime(self);
       // проиграть анимацию T_WARN
       AI_PlayAni(self,"T_WARN");
   };
   // если НПС завершил преследование цели
   if(self.aivar[AIV_PursuitEnd] == TRUE)
   {
       // если расстояние между НПС и целью > диапазона чувствительности НПС
       if(Npc_GetDistToNpc(self,other) > self.senses_range)
       {
           return LOOP_END;
       };
       // если текущее время > предыдущего времени (прошла как минимум секунда)
       if (Npc_GetStateTime (self) > self.aivar[AIV_StateTime])
       {
           // если расстояние между НПС и целью меньше дистанции, когда НПС прекратил преследование
           if(Npc_GetDistToNpc(self,other) < self.aivar[AIV_Dist])
           // или (цель не бежит и не прыгает)
           || ((!C_BodyStateContains(other,BS_RUN)) && (!C_BodyStateContains(other,BS_JUMP)))
           {
               // сброс флага окончания преследования
               self.aivar[AIV_PursuitEnd] = FALSE;
               // сброс времени нахождения НПС в этом состоянии
               Npc_SetStateTime(self,0);
               // сброс счетчика времени нахождения в состоянии
               self.aivar[AIV_StateTime] = 0;
           }
           else
           {
               // НПС поворачивается к цели
               AI_TurnToNpc(self,other);
               // получить дистанцию до цели
               self.aivar[AIV_Dist] = Npc_GetDistToNpc(self,other);
               // обновить время состояния НПС
               self.aivar[AIV_StateTime] = Npc_GetStateTime(self);
           };
       };
       return LOOP_CONTINUE;
   };
   // если (цель плывет или ныряет) и НПС не преследует цель в воде
   if(C_BodyStateContains(other,BS_SWIM) || C_BodyStateContains(other,BS_DIVE)) && (self.aivar[AIV_MM_FollowInWater] == FALSE)
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       return LOOP_END;
   };
   // если НПС выжидает перед атакой
   if(self.aivar[AIV_WaitBeforeAttack] == 1)
   {
       // ожидание 0.8 сек
       AI_Wait(self,0.8 );
       // сброс флага задержки
       self.aivar[AIV_WaitBeforeAttack] = 0;
   };
   // если уровень НПС == 0 (вызванный монстр)
   if(self.level == 0)
   {
       // если текущее время > предыдущего времени (прошла как минимум секунда)
       if(Npc_GetStateTime(self) > self.aivar[AIV_StateTime])
       {
           // время от начала вызова монстра ++
           self.aivar[AIV_SummonTime] = (self.aivar[AIV_SummonTime] + 1);
           // запомнить текущее время
           self.aivar[AIV_StateTime] = Npc_GetStateTime(self);
       };
       // если время от начала вызова монстра >= времени жизни вызванных монстров
       if(self.aivar[AIV_SummonTime] >= MONSTER_SUMMON_TIME)
       {
           // вызванный монстр умирает
           Npc_ChangeAttribute(self,ATR_HITPOINTS,-self.attribute[ATR_HITPOINTS_MAX]);
       };
   };
   // если цель не бежит и не прыгает и время в состоянии > 0
   if((!C_BodyStateContains(other,BS_RUN)) && (!C_BodyStateContains(other,BS_JUMP))) && (Npc_GetStateTime(self) > 0)
   {
       // сброс времени текущего состояния
       Npc_SetStateTime(self,0);
       // сброс счетчика времени нахождения в состоянии
       self.aivar[AIV_StateTime] = 0;
   };
   // если дистанция атаки > 0
   if(self.aivar[AIV_MaxDistToWp] > 0)
   {
       // если расстояние между НПС и вайпоинтом НПС > дистанции атаки и расстояние между целью и вайпоинтом НПС > дистанции акаки
       if(Npc_GetDistToWP(self,self.wp) > self.aivar[AIV_MaxDistToWp]) && (Npc_GetDistToWP(other,self.wp) > self.aivar[AIV_MaxDistToWp])
       {
           // установить НПС тактику защитников крепостных стен
           self.fight_tactic = FAI_NAILED;
       }
       else // иначе (если НПС или цель близка к вайпоинту НПС)
       {
           // установить НПС родную тактику сражения
           self.fight_tactic = self.aivar[AIV_OriginalFightTactic];
       };
   };
   // если агрессор маг или
   if(C_NpcIsMonsterMage(self))
   || (self.guild == GIL_SKELETON)             // скелет
   || (self.guild == GIL_SUMMONED_SKELETON)    // или вызванный скелет
   || (self.guild > GIL_SEPERATOR_ORC)         // или орк
   {
       // создать боеприпасы
       B_CreateAmmo(self);
       // установить полную ману
       Npc_ChangeAttribute(self,ATR_MANA,ATR_MANA_MAX);
       // выбрать оружие, сответствующее цели
       B_SelectWeapon(self,other);
   };
   // если цель существует и не обездвижена
   if(Hlp_IsValidNpc(other)) && (!C_NpcIsDown(other))
   {
       // если цель не в состоянии разговора
       if(other.aivar[AIV_INVINCIBLE] == FALSE)
       {
           // НПС атакует цель (Примечание: функция AI_Attack работает только когда очередь AI состояний НПС пуста)
           AI_Attack(self);
       }
       else
       {
           // очистка очереди AI состояний НПС
           Npc_ClearAIQueue(self);
       };
       // запись НПС id последней цели
       self.aivar[AIV_LASTTARGET] = Hlp_GetInstanceID(other);
       return LOOP_CONTINUE;
   }
   else // иначе (если цель отсутствует или обездвижена)
   {
       // если приоритет агрессора на пожирание падали и агрессор может есть цель
       if(self.aivar[AIV_MM_PRIORITY] == PRIO_EAT) && (C_WantToEat(self,other))
       {
           // очистка очереди AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает
           AI_StandUp(self);
           return LOOP_END;
       };
       // разрешение воспринимать все объекты в зоне действия восприятий
       Npc_PerceiveAll(self);
       // поиск новой цели
       Npc_GetNextTarget(self);
       // если цель найдена и она не обездвижена
       if(Hlp_IsValidNpc(other)) && (!C_NpcIsDown(other))
       // и (расстояние между НПС и целью < дистанции действия восприятий или цель ГГ)
       && ((Npc_GetDistToNpc(self,other) < PERC_DIST_INTERMEDIAT) || (Npc_IsPlayer(other)))
       // и цель не в состоянии разговора
       && (other.aivar[AIV_INVINCIBLE] == FALSE)
       {
           // запись НПС id последней цели
           self.aivar[AIV_LASTTARGET] = Hlp_GetInstanceID(other);
           return LOOP_CONTINUE;
       }
       else // иначе
       {
           // очистка очереди AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает
           AI_StandUp(self);
           return LOOP_END;
       };
   };
};

// Завершение состояния атаки
func void ZS_MM_Attack_End()
{
   // установить other на последнюю цель
   other = Hlp_GetNpc(self.aivar[AIV_LASTTARGET]);
   // если агрессор маг или
   if(C_NpcIsMonsterMage(self))
   || (self.guild == GIL_SKELETON)             // скелет
   || (self.guild == GIL_SUMMONED_SKELETON)    // или вызванный скелет
   || (self.guild > GIL_SEPERATOR_ORC)         // или орк
   {
       // НПС убирает оружие
       AI_RemoveWeapon(self);
   };
   // если цель убита и агрессор может пожирать цель
   if(Npc_IsDead(other)) && (C_WantToEat(self,other))
   {
       // очистка очереди AI состояний НПС
       Npc_ClearAIQueue(self);
       // переход в состояние пожирания добычи
       AI_StartState(self,ZS_MM_EatBody,0,"");
       return;
   };
};

// *************************************************
// Обработчик состояния когда монстр поедает добычу
// -------------------------------------------------
// self - НПС монстр, other - добыча (труп)
// *************************************************

// Инициализация состояния
func void ZS_MM_EatBody()
{
   // установка времени реакции на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_MM_AssessDamage);
   // разрешить восприятие повреждения, нанесенного другому НПС
   Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,B_MM_AssessOthersDamage);
   // разрешить восприятие смерти
   Npc_PercEnable(self,PERC_ASSESSMURDER,B_MM_AssessOthersDamage);
   // монстр идет к телу
   AI_GotoNpc(self,other);
   // монстр поворачивается к добыче
   AI_TurnToNpc(self,other);
   // проигрывается анимация поедания
   AI_PlayAni(self,"T_STAND_2_EAT");
   // установить приоритет на поедание
   self.aivar[AIV_MM_PRIORITY] = PRIO_EAT;
   // ссылка на последнее съеденное тело
   self.aivar[AIV_LASTBODY] = Hlp_GetInstanceID(other);
   // сброс флага восприятия врага
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Цикл состояния
func int ZS_MM_EatBody_loop()
{
   // если восприятие врага выключено
   if (self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // разрешить восприятие врага
       Npc_PercEnable(self,PERC_ASSESSENEMY,B_MM_AssessEnemy);
       // установить флаг разрешения восприятия
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   // если добыча съедена
   if(!Hlp_IsValidNpc(other))
   {
       // очистка очереди AI состояний монстра
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_EatBody_end()
{
   // прекратить анимацию поедания
   AI_PlayAni(self,"T_EAT_2_STAND");
};

//****************************************************
// Обработчик состояния когда монстр убегает от врага
//----------------------------------------------------
// self - монстр, other - враг
//****************************************************

// Инициализация состояния
func void ZS_MM_Flee()
{
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // инициализировать переменную other врагом
   B_ValidateOther();
   // монстр встает
   AI_StandUp(self);
   // монстр переходит в режим бега
   AI_SetWalkmode(self,NPC_RUN);
   // монстру посылается предупреждение от врага
   Npc_SendPassivePerc(self,PERC_ASSESSWARN,other,self);
   // рассинхронизация [Примечание: мне непонятно зачем?]
   B_MM_DeSynchronize();
};

// Цикл состояния
func int ZS_MM_Flee_Loop()
{
   // обновить ссылку на врага
   Npc_GetTarget(self);
   // если расстояние между монстром и врагом < 20 метров
   if(Npc_GetDistToNpc(self,other) < 2000)
   {
       // если прошла одна секунда
       if(Npc_GetStateTime(self) > 0)
       {
           // враг посылает монстру предупреждение
           Npc_SendPassivePerc(self,PERC_ASSESSWARN,other,self);
           // сброс времени состояния
           Npc_SetStateTime(self,0);
       };
       // монстр убегает от врага
       AI_Flee(self);
       return LOOP_CONTINUE;
   }
   else
   {
       // очистка очереди AI состояний монстра
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
};

// Завершение состояния
func void ZS_MM_Flee_End()
{
};

// ********************************************
// Обработчик состояния охоты монстра
// --------------------------------------------
// self - монстр, other - жертва
// ********************************************

// Инициализация состояния охоты
func void ZS_MM_Hunt()
{
   // разрешить базовые восприятия монстров
   Perception_Set_Monster_Rtn();
   // монстр встает
   AI_StandUp(self);
   // поворачивается к жертве
   AI_TurnToNpc(self,other);
   // переходит на шаг
   AI_SetWalkmode(self,NPC_WALK);
   // идет к жертве
   AI_GotoNpc(self,other);
};

// Цикл состояния
func int ZS_MM_Hunt_Loop()
{
   return LOOP_END;
};

// Завершение состояния
func void ZS_MM_Hunt_End()
{
};

// **************************************************
// Обработчик состояния когда монстр угрожает врагу
// --------------------------------------------------
// self - монстр, other - враг
// **************************************************

// Инициализация состояния угрозы
func void ZS_MM_ThreatenEnemy()
{
   // установка времени реакции на восприятия 2 сек
   Npc_SetPercTime(self,2);
   // разрешить восприятие мертвого тела
   Npc_PercEnable(self,PERC_ASSESSBODY,B_MM_AssessBody);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_MM_AssessDamage);
   // разрешить восприятие повреждения, нанесенного другому НПС
   Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,B_MM_AssessOthersDamage);
   // разрешить восприятие смерти
   Npc_PercEnable(self,PERC_ASSESSMURDER,B_MM_AssessOthersDamage);
   // монстр встает
   AI_StandUp(self);
   // враг посылает монстру предупреждение
   Npc_SendPassivePerc(self,PERC_ASSESSWARN,other,self);
};

// Цикл состояния угрозы
func int ZS_MM_ThreatenEnemy_loop()
{
   // если расстояние между монстром и врагом > дистанции действия активных восприятий
   if(Npc_GetDistToNpc(self,other) > PERC_DIST_MONSTER_ACTIVE_MAX)
   {
       // очистка очереди AI состояний монстра
       Npc_ClearAIQueue(self);
       return LOOP_END;
   };
   // если расстояние между монстром и врагом <= дистанции начала атаки
   if(Npc_GetDistToNpc(self,other) <= FIGHT_DIST_MONSTER_ATTACKRANGE)
   {
       // очистка очереди AI состояний монстра
       Npc_ClearAIQueue(self);
       // установка цели для монстра
       Npc_SetTarget(self, other);
       // переход в состояние атаки
       AI_StartState(self,ZS_MM_Attack,0,"");
       return LOOP_END;
   };
   // если время в состоянии >= времени угрозы
   if(Npc_GetStateTime(self) >= MONSTER_THREATEN_TIME)
   {
       Npc_ClearAIQueue(self);
       Npc_SetTarget(self,other);
       // переход в состояние атаки
       AI_StartState(self,ZS_MM_Attack,0,"");
       return LOOP_END;
   };
   // монстр поворачивается к врагу
   AI_TurnToNpc(self,other);
   // играется анимация угрозы
   AI_PlayAni(self,"T_WARN");
   return LOOP_CONTINUE;
};

// Завершение состояния угрозы
func void ZS_MM_ThreatenEnemy_end()
{
};
Системная функция AI_StartState может управлять последовательностью выполнения фаз состояний, за это отвечает параметр stateBehaviour, если он = 0, то сразу выполняется переход в указанное состояние, если он = 1, то переход в новое состояние выполнится только после полного завершения текущего состояния.


10. Функции распорядка дня людей.

Все функции распорядка дня людей регистрируются в файле ..\AI\Human\TA.d
Шаблон регистрации любой функции:
func void TA_name(var int start_h,var int start_m,var int stop_h,var int stop_m,var string waypoint)
{
     // регистрация обработчика состояния ZS_name с временами начала и конца работы в точке waypoint
     TA_Min(self,start_h,start_m,stop_h,stop_m,ZS_name,waypoint);
};
где:
name - имя выполняемой функции,
start_h - час начала выполнения,
start_m - минута начала выполнения,
stop_h - час окончания выполнения,
stop_m - минута окончания выполнения,
waypoint - имя WP точки выполнения функции

Сами обработчики состояний функций распорядка дня людей находятся в директории ..\AI\Human\TA_Human\
Понятно, что вызов всех обработчиков состояний производится ядром системы в заданном интервале времени.
Обработчики состояний функций распорядка дня я рассматривать не буду, но если кому-то интересны некоторые обработчики, можно сделать заказ на их рассмотрение.


11. Функции распорядка дня монстров.

Как таковой (по сравнению с людьми) распорядок дня у монстров отсутствует, есть функция планировщика суточного цикла монстров ZS_MM_AllScheduler, которая вызывает обработчики конкретных состояний.
Все файлы обработчиков суточных состояний монстров расположены в директории ..\AI\Monster\RTN_Monster
// **************************************
// Планировщик суточного цикла монстров
// **************************************

func void ZS_MM_AllScheduler()
{
     // установить приоритет на питание вместо атаки
     self.aivar[AIV_MM_PRIORITY] = PRIO_EAT;
     // если НПС Каменный страж и Равен убит
     if(self.guild == GIL_STONEGUARDIAN) && (RavenIsDead == TRUE)
     {
           // убить Каменного стража
           B_KillNpc(self);
     };
     // если наступило время сна монстра или время контролируется программой поведения монстра
     if(Wld_IsTime(self.aivar[AIV_MM_SleepStart],00,self.aivar[AIV_MM_SleepEnd]
,00) || (self.aivar[AIV_MM_SleepStart] == OnlyRoutine))
     {
           // перейти в состояние сна (обратить внимание на параметр stateBehaviour = 1, переход в состояние сна возможен только после полного завершения текущего состояния)
           AI_StartState(self,ZS_MM_Rtn_Sleep,1,"");
     }
     // иначе, если наступило время отдыха монстра или время контролируется программой поведения монстра
     else if(Wld_IsTime(self.aivar[AIV_MM_RestStart],00,self.aivar[AIV_MM_RestEnd],00) || (self.aivar[AIV_MM_RestStart] == OnlyRoutine))
     {
           // перейти в состояние отдыха
           AI_StartState(self,ZS_MM_Rtn_Rest,1,"");
     }
     // иначе, если наступило время брожения монстра или время контролируется программой поведения монстра
     else if(Wld_IsTime(self.aivar[AIV_MM_RoamStart],00,self.aivar[AIV_MM_RoamEnd],00) || (self.aivar[AIV_MM_RoamStart] == OnlyRoutine))
     {
           // перейти в состояние брожения
           AI_StartState(self,ZS_MM_Rtn_Roam,1,"");
     }
     // иначе, если наступило время кормежки монстра или время контролируется программой поведения монстра
     else if(Wld_IsTime(self.aivar[AIV_MM_EatGroundStart],00,self.aivar[AIV_MM_EatGroundEnd],00) || (self.aivar[AIV_MM_EatGroundStart] == OnlyRoutine))
     {
           // перейти в состояние кормежки
           AI_StartState(self,ZS_MM_Rtn_EatGround,1,"");
     }
     // иначе, если наступило время резвости монстра или время контролируется программой поведения монстра
     else if(Wld_IsTime(self.aivar[AIV_MM_WuselStart],00,self.aivar[AIV_MM_WuselEnd],00) || (self.aivar[AIV_MM_WuselStart] == OnlyRoutine))
     {
           // перейти в состояние резвости
           AI_StartState(self,ZS_MM_Rtn_Wusel,1,"");
     }
     // иначе, если наступило время посидеть орку или время контролируется программой поведения монстра
     else if(Wld_IsTime(self.aivar[AIV_MM_OrcSitStart],00,self.aivar[AIV_MM_OrcSitEnd],00) || (self.aivar[AIV_MM_OrcSitStart] == OnlyRoutine))
     {
           // перейти орку в сидячее состояние
           AI_StartState(self,ZS_MM_Rtn_OrcSit,1,"");
     }
     else // иначе (по умолчанию)
     {
           // перейти в состояние отдыха
           AI_StartState(self,ZS_MM_Rtn_Rest,1,"");
     };
};

// *********************************
// Обработчик состояния сна монстра
// *********************************

// Функция обработки локального восприятия тихих звуков во время сна
func void B_MM_AssessQuietSound_Sleep()
{
     // если спящий монстр услышал врага
     if(Wld_GetGuildAttitude(self.guild,other.guild) == ATT_HOSTILE)
     {
           // реакция монстра на врага
           B_MM_AssessEnemy();
     };
};

// Инициализация состояния сна
func void ZS_MM_Rtn_Sleep()
{
     // разрешить восприятие магии
     Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
     // разрешить восприятие повреждения
     Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_MM_AssessDamage);
     // разрешить восприятие повреждения, нанесенного другому НПС
     Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,B_MM_AssessOthersDamage);
     // разрешить восприятие смерти
     Npc_PercEnable(self,PERC_ASSESSMURDER,B_MM_AssessOthersDamage);
     // разрешить восприятие предупреждения
     Npc_PercEnable(self,PERC_ASSESSWARN,B_MM_AssessWarn);
     // разрешить восприятие тихих звуков (шаги, звон падающего предмета)
     Npc_PercEnable(self,PERC_ASSESSQUIETSOUND,B_MM_AssessQuietSound_Sleep);
     // установить режим передвижения шагом
     AI_SetWalkmode       (self, NPC_WALK);
     B_MM_DeSynchronize();
     // если монстр не находится в заданной точке
     if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
     {
           // монстр идет в заданную точку
           AI_GotoWP(self,self.wp);
     };
     // если есть свободная точка с именем "FP_ROAM"
     if(Wld_IsFPAvailable(self,"FP_ROAM"))
     {
           // монстр идет в эту точку
           AI_GotoFP(self,"FP_ROAM");
     };
     // анимация "T_PERCEPTION"
     AI_PlayAni(self,"T_PERCEPTION");
     // анимация перехода ко сну
     AI_PlayAniBS(self,"T_STAND_2_SLEEP",BS_LIE);
};

// Цикл состояния сна
func int ZS_MM_Rtn_Sleep_loop()
{
     // если время сна истекло или время не контролируется программой поведения монстра
     if((!Wld_IsTime(self.aivar[AIV_MM_SleepStart],00,self.aivar[AIV_MM_SleepEnd],00)) && (self.aivar[AIV_MM_SleepStart] != OnlyRoutine))
     {
           // переход в планировщик суточного цикла
           AI_StartState(self,ZS_MM_AllScheduler,1,"");
           return LOOP_END;
     };
     return LOOP_CONTINUE;
};

// Завершение состояния сна
func void ZS_MM_Rtn_Sleep_end()
{
     // анимация пробуждения
     AI_PlayAniBS(self,"T_SLEEP_2_STAND",BS_STAND);
};

// *************************************
// Обработчик состояния отдыха монстров
// *************************************

// Инициализация состояния отдыха
func void ZS_MM_Rtn_Rest()
{
     // разрешить нормальный набор восприятий
     Perception_Set_Monster_Rtn();
     // режим пережвижения шагом
     AI_SetWalkmode(self,NPC_WALK);
     B_MM_DeSynchronize();
     // если монстр не находится в заданной точке
     if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
     {
           // монстр идет в заданную точку
           AI_GotoWP(self,self.wp);
     };
     // сброс флага достижения точки отдыха
     self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Цикл состояния отдыха
func int ZS_MM_Rtn_Rest_Loop()
{
     // если НПС Каменный страж и Равен убит
     if(self.guild == GIL_STONEGUARDIAN) && (RavenIsDead == TRUE)
     {
           // убить Каменного стража
           B_KillNpc(self);
     };
     // если время отдыха истекло или время не контролируется программой поведения монстра
     if((!Wld_IsTime(self.aivar[AIV_MM_RestStart],00,self.aivar[AIV_MM_RestEnd],00)) && self.aivar[AIV_MM_RestStart] != OnlyRoutine))
     {
           // переход в планировщик суточного цикла
           AI_StartState(self,ZS_MM_AllScheduler,1,"");
           return LOOP_END;
     };
     // если точка отдыха не достигнута
     if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
     {
     // если есть свободная точка с именем "FP_ROAM"
           if(Wld_IsFPAvailable(self,"FP_ROAM"))
           {
                 // монстр идет в эту точку
                 AI_GotoFP(self,"FP_ROAM");
           };
           // если монстр дошел до точки
           if(Npc_IsOnFP(self,"FP_ROAM"))
           {
                 // установить флаг достижения точки отдыха
                 self.aivar[AIV_TAPOSITION] = ISINPOS;
           };
     }
     else // иначе (монстр находится в точке отдыха)
     {
           // с вероятностью 0,005 менять анимацию монстра
           if(Hlp_Random(1000) <= 5)
           {
                 var int randomMove;
                 randomMove = Hlp_Random(3);
                 if(randomMove == 0) { AI_PlayAni(self,"R_ROAM1"); };
                 if(randomMove == 1) { AI_PlayAni(self,"R_ROAM2"); };
                 if(randomMove == 2) { AI_PlayAni(self,"R_ROAM3"); };
           };
     };
     return LOOP_CONTINUE;
};

// Завершение состояния отдыха
func void ZS_MM_Rtn_Rest_End()
{
};

// **************************************
// Обработчик состояния брожения монстра
// **************************************

// Инициализация состояния
func void ZS_MM_Rtn_Roam()
{
   // разрешить нормальный набор восприятий
   Perception_Set_Monster_Rtn();
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   B_MM_DeSynchronize();
   // если монстр не находится в заданной точке
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // монстр идет в заданную точку
       AI_GotoWP(self,self.wp);
   };
   // сброс флага чередования циклов (стоит или бродит)
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Цикл состояния брожения
func int ZS_MM_Rtn_Roam_loop()
{
   // если время брожения истекло и время не контролируется программой поведения монстра
   if((!Wld_IsTime(self.aivar[AIV_MM_RoamStart],00,self.aivar[AIV_MM_RoamEnd],00)) && (self.aivar[AIV_MM_RoamStart] != OnlyRoutine))
   {
       // переход в планировщик суточного цикла
       AI_StartState(self,ZS_MM_AllScheduler,1,"");
       return LOOP_END;
   };
   // фаза передвижения завершена
   if (self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       var int wanderTime;
       // время стояния
       wanderTime = Hlp_Random(5);
       // сброс времени цикла
       Npc_SetStateTime(self,0);
       // установить флаг стояния
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   // если время стояния закончилось
   if(Npc_GetStateTime(self) > wanderTime)
   {
       // если есть свободная точка "FP_ROAM"
       if(Wld_IsNextFPAvailable(self,"FP_ROAM"))
       {
           // монстр идет в эту мочку
           AI_GotoNextFP(self,"FP_ROAM");
       };
       // сброс флага стояния
       self.aivar[AIV_TAPOSITION] = NOTINPOS;
   }
   else // иначе (стояние)
   {
       // с вероятностью 0,005 менять анимацию монстра
       if(Hlp_Random(1000) <= 5)
       {
           var int randomMove;
           randomMove = Hlp_Random(3);
           if(randomMove == 0) { AI_PlayAni(self,"R_ROAM1"); };
           if(randomMove == 1) { AI_PlayAni(self,"R_ROAM2"); };
           if(randomMove == 2) { AI_PlayAni(self,"R_ROAM3"); };
       };
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_Rtn_Roam_end()
{
};

// **************************************
// Обработчик состояния кормежки монстра
// **************************************

// Инициализация состояния кормежки
func void ZS_MM_Rtn_EatGround()
{
   // разрешить нормальный набор восприятий
   Perception_Set_Monster_Rtn();
   // режим пережвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   B_MM_DeSynchronize();
   // если монстр не находится в заданной точке
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // монстр идет в заданную точку
       AI_GotoWP(self,self.wp);
   };
   // если есть свободная точка с именем "FP_ROAM"
   if(Wld_IsFPAvailable(self,"FP_ROAM"))
   {
       // монстр идет в эту точку
       AI_GotoFP(self,"FP_ROAM");
   };
   // анимация перехода в состояние кормежки
   AI_PlayAni(self,"T_STAND_2_EAT");
   // анимация кормежки
   Mdl_ApplyRandomAni(self,"S_EAT","R_ROAM1");
   Mdl_ApplyRandomAni(self,"S_EAT","R_ROAM2");
   Mdl_ApplyRandomAni(self,"S_EAT","R_ROAM3");
   Mdl_ApplyRandomAniFreq(self,"S_EAT",8.0);
};

// Цткл состояния кормежки
func int ZS_MM_Rtn_EatGround_Loop()
{
   // если время кормежки истекло и время не контролируется программой поведения монстра
   if((!Wld_IsTime(self.aivar[AIV_MM_EatGroundStart],00,self.aivar[AIV_MM_EatGroundEnd],00)) && (self.aivar[AIV_MM_EatGroundStart] != OnlyRoutine))
   {
       // переход в планировщик суточного цикла
       AI_StartState(self,ZS_MM_AllScheduler,1,"");
       return LOOP_END;
   };
   return LOOP_CONTINUE;
};

// Завершение состояния кормежки
func void ZS_MM_Rtn_EatGround_End()
{
   // анимация выхода из состояния кормежки
   AI_PlayAni(self,"T_EAT_2_STAND");
};

// **************************************
// Обработчик состояния резвости монстра
// **************************************

// Инициализация состояния резвости
func void ZS_MM_Rtn_Wusel()
{
   // разрешить нормальный набор восприятий
   Perception_Set_Monster_Rtn();
   // режим пережвижения бегом
   AI_SetWalkmode(self,NPC_RUN);
   // если монстр не находится в заданной точке
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // монстр бежит в заданную точку
       AI_GotoWP(self,self.wp);
   };
   // монстр бежит на свободную точку "FP_ROAM"
   AI_GotoFP(self,"FP_ROAM");
};

// Цикл состояния резвости
func int ZS_MM_Rtn_Wusel_loop()
{
   // если время резвости истекло и время не контролируется программой поведения монстра
   if((!Wld_IsTime(self.aivar[AIV_MM_WuselStart],00,self.aivar[AIV_MM_WuselEnd],00)) && (self.aivar[AIV_MM_WuselStart] != OnlyRoutine))
   {
       // переход в планировщик суточного цикла
       AI_StartState(self,ZS_MM_AllScheduler,1,"");
       return LOOP_END;
   };
   // если монстр находится в этом состоянии >= 1 сек
   if(Npc_GetStateTime (self) >= 1)
   {
       // если монстр находится в точке "FP_ROAM"
       if(Npc_IsOnFP(self,"FP_ROAM"))
       {
           // поиск следующей свободной точки
           if(Wld_IsNextFPAvailable(self,"FP_ROAM"))
           {
               // очистка очереди AI состояний монстра
               Npc_ClearAIQueue(self);
               // переход на новую свободную точку
               AI_GotoNextFP(self,"FP_ROAM");
           };
       }
       else // если не дошел до точки
       {
           // если монстр не идет и не бежит
           if(!C_BodyStateContains(self,BS_WALK)) && (!C_BodyStateContains(self,BS_RUN))
           {
               // переход в свободную точку
               AI_GotoFP(self,"FP_ROAM");
           };
       };
       // сброс времени цикла
       Npc_SetStateTime(self,0);
       // здесь лишний (можно удалить)
       self.aivar[AIV_TAPOSITION] = NOTINPOS;
   };
   return LOOP_CONTINUE;
};

// Завершение состояния резвости
func void ZS_MM_Rtn_Wusel_end()
{
};

// ************************************
// Обработчик сидячего состояния орков
// ************************************

// Инициализация состояния
func void ZS_MM_Rtn_OrcSit()
{
   // разрешить нормальный набор восприятий
   Perception_Set_Monster_Rtn();
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   B_MM_DeSynchronize();
   // если монстр не находится в заданной точке
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // монстр идет в заданную точку
       AI_GotoWP(self,self.wp);
   };
   // сброс флага достижения точки сидения
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Цикл состояния
func int ZS_MM_Rtn_OrcSit_loop()
{
   // если время сидения истекло и время не контролируется программой поведения монстра
   if((!Wld_IsTime(self.aivar[AIV_MM_OrcSitStart],00,self.aivar[AIV_MM_OrcSitEnd],00)) && (self.aivar[AIV_MM_OrcSitStart] != OnlyRoutine))
   {
       // переход в планировщик суточного цикла
       AI_StartState(self,ZS_MM_AllScheduler,1,"");
       return LOOP_END;
   };
   // если точка сидения не достигнута
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // если есть свободная точка "FP_CAMPFIRE"
       if(Wld_IsFPAvailable(self,"FP_CAMPFIRE"))
       {
           // монстр идет в эту точку
           AI_GotoFP(self,"FP_CAMPFIRE");
       };
       // если монстр находится в точке "FP_CAMPFIRE"
       if(Npc_IsOnFP(self,"FP_CAMPFIRE"))
       {
           // анимация сидящего орка
           AI_PlayAniBS(self,"T_STAND_2_GUARDSLEEP",BS_SIT);
           // установка флага достижения точки
           self.aivar[AIV_TAPOSITION] = ISINPOS;
       };
   }
   else // лишнее, можно удалить!
   {
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_Rtn_OrcSit_end()
{
   // анимация выхода из сидячего положения
   AI_PlayAniBS(self,"T_GUARDSLEEP_2_STAND",BS_STAND);
};
Несколько обработчиков состояний для некоторых типов монстров.
// ************************************
// Обработчик состояния отдыха дракона
// ************************************

// Инициализация состояния
func void ZS_MM_Rtn_DragonRest()
{
   // установить время реакции на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // приоритет на питание
   self.aivar[AIV_MM_PRIORITY] = PRIO_EAT;
   // если дракон не тестовый (строки программы, отмеченные в конце //*** можно удалить)
   if(Hlp_GetInstanceID(self) != Hlp_GetInstanceID(Dragon_Testmodell)) //***
   {                                                                   //***
       // разрешить нормальный набор восприятий
       Perception_Set_Monster_Rtn();
       // разрешить восприятие ГГ
       Npc_PercEnable(self,PERC_ASSESSPLAYER,B_MM_AssessPlayer);
   };                                                                  //***
   // разрешить восприятие разговора
   Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   B_MM_DeSynchronize();
   // если монстр не находится в заданной точке
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // монстр идет в заданную точку
       AI_GotoWP(self,self.wp);
   };
   // если есть свободная точка с именем "FP_ROAM"
   if(Wld_IsFPAvailable(self,"FP_ROAM"))
   {
       // монстр идет в эту точку
       AI_GotoFP(self,"FP_ROAM");
   }
   else // иначе
   {
       // выравнивание в точке
       AI_AlignToWP(self);
   };
   // сброс счетчика цикла регенерации
   self.aivar[AIV_TAPOSITION] = 0;
};

// Цикл состояния
func int ZS_MM_Rtn_DragonRest_Loop()
{
   // если время отдыха истекло и время не контролируется программой поведения монстра
   if((!Wld_IsTime(self.aivar[AIV_MM_RestStart],00,self.aivar[AIV_MM_RestEnd],00)) && (self.aivar[AIV_MM_RestStart] != OnlyRoutine))
   {
       // переход в планировщик суточного цикла
       AI_StartState(self,ZS_MM_AllScheduler,1,"");
       return LOOP_END;
   };
   // если НПС дракон
   if(self.guild == GIL_DRAGON)
   {
       // счетчик цикла регенерации ++
       self.aivar[AIV_TAPOSITION] += 1;
       // если жизнь дракона < максимальной и прошло не менее 2 сек
       if(self.attribute[ATR_HITPOINTS] < self.attribute[ATR_HITPOINTS_MAX]) && (self.aivar[AIV_TAPOSITION] >= 2)
       {
           // жизнь дракона ++
           self.attribute[ATR_HITPOINTS] += 1;
           // сброс счетчика цикла регенерации
           self.aivar[AIV_TAPOSITION] = 0;
       };
   };
   // с вероятностью 0,005 менять анимацию монстра
   if(Hlp_Random(1000) <= 5)
   {
       var int randomMove;
       randomMove = Hlp_Random(3);
       AI_StandUp(self);
       if(randomMove == 0) { AI_PlayAni(self,"R_ROAM1"); };
       if(randomMove == 1) { AI_PlayAni(self,"R_ROAM2"); };
       if(randomMove == 2) { AI_PlayAni(self,"R_ROAM3"); };
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_Rtn_DragonRest_End()
{
   // анимация выхода из отдыха
   AI_PlayAni(self,"T_REST_2_STAND");
};

// *******************************************
// Обработчик состояния когда овца идет за ГГ
// *******************************************

// Инициализация состояния
func void ZS_MM_Rtn_Follow_Sheep()
{
   // установить время реакции на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // разрешить восприятие ГГ
   Npc_PercEnable(self,PERC_ASSESSPLAYER,B_MM_AssessPlayer);
   // разрешить восприятие разговора
   Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
};

// Цикл состояния
func int ZS_MM_Rtn_Follow_Sheep_Loop()
{
   // если НПС член партии ГГ
   if(self.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       // если расстояние между НПС и ГГ > 5 метров
       if(Npc_GetDistToNpc(self,hero) > 500)
       {
           // если НПС не плывет
           if(!C_BodyStateContains(self,BS_SWIM))
           {
               // НПС переходит на бег
               AI_SetWalkmode(self,NPC_RUN);
           };
           // НПС движется к ГГ
           AI_GotoNpc(self,hero);
       }
       else // иначе
       {
           // НПС поворачивается к ГГ
           AI_TurnToNpc(self,hero);
           // установить для НПС ближайшую свободную точку
           self.wp = Npc_GetNearestWP(self);
       };
   }
   else // иначе
   {
       // анимация
       var int randomMove;
       randomMove = Hlp_Random(3);
       if(randomMove == 0) { AI_PlayAni(self,"R_ROAM1"); };
       if(randomMove == 1) { AI_PlayAni(self,"R_ROAM2"); };
       if(randomMove == 2) { AI_PlayAni(self,"R_ROAM3"); };
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_Rtn_Follow_Sheep_End()
{
};

// ****************************************************
// Обработчик состояния когда овца идет за Бальтасаром
// ****************************************************

// Инициализация состояния
func void ZS_MM_Rtn_Follow_Sheep_Balthasar()
{
   // установить время реакции на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // разрешить восприятие ГГ
   Npc_PercEnable(self,PERC_ASSESSPLAYER,B_MM_AssessPlayer);
};

// Цикл состояния
func int ZS_MM_Rtn_Follow_Sheep_Balthasar_Loop()
{
   // если расстояние между Бальтасаром и точкой "NW_BIGMILL_FARM3_BALTHASAR" > 5 м
   if(Npc_GetDistToWP(Balthasar,"NW_BIGMILL_FARM3_BALTHASAR") > 500)
   {
       // если расстояние между НПС и Бальтасаром > 5 метров
       if(Npc_GetDistToNpc(self,Balthasar) > 500)
       {
           // если НПС не плывет
           if(!C_BodyStateContains(self,BS_SWIM))
           {
               // НПС переходит на бег
               AI_SetWalkmode(self,NPC_RUN);
           };
           // НПС движется к Бальтасару
           AI_GotoNpc(self,Balthasar);
       }
       else // иначе
       {
           // НПС поворачивается к Бальтасару
           AI_TurnToNpc(self,Balthasar);
           // установить для НПС ближайшую свободную точку
           self.wp = Npc_GetNearestWP(self);
       };
   }
   else // иначе (когда пришли)
   {
       // переход НПС в состояние брожения около точки "NW_BIGMILL_FARM3_BALTHASAR"
       AI_StartState(self,ZS_MM_Rtn_Roam,1,"NW_BIGMILL_FARM3_BALTHASAR"
);
   };
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_Rtn_Follow_Sheep_Balthasar_End ()
{
};

// ****************************************
// Обработчик поведения вызванного монстра
// ****************************************

// Локальное восприятие разговора с ГГ
func void B_SummonedAssessTalk()
{
   // вызванный монстр умирает (жизнь = 0)
   Npc_ChangeAttribute(self,ATR_HITPOINTS,-self.attribute[ATR_HITPOINTS_MAX])
;
};

// Инициализация состояния
func void ZS_MM_Rtn_Summoned()
{
   // установить время реакции на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // разрешить восприятие ГГ
   Npc_PercEnable(self,PERC_ASSESSPLAYER,B_MM_AssessPlayer);
   // разрешить восприятие врага
   Npc_PercEnable(self,PERC_ASSESSENEMY,B_MM_AssessEnemy);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_MM_AssessDamage);
   // разрешить восприятие сражения двух других НПС
   Npc_PercEnable(self,PERC_ASSESSFIGHTSOUND,B_MM_AssessOthersDamage);
   // если вызванный НПС Огонек
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Wisp_Detector))
   {
       // разрешить восприятие разговора
       Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   }
   else // иначе (если монстр)
   {
       // разрешить локальное восприятие разговора
       Npc_PercEnable(self,PERC_ASSESSTALK,B_SummonedAssessTalk);
   };
   // установить дружеское отношение к ГГ
   B_SetAttitude(self,ATT_FRIENDLY);
   // НПС член партии ГГ
   self.aivar[AIV_PARTYMEMBER] = TRUE;
   // НПС встает
   AI_StandUp(self);
   // режим передвижения - бег
   AI_SetWalkmode(self,NPC_RUN);
};

// Цикл состояния
func int ZS_MM_Rtn_Summoned_Loop()
{
   // Выполнение Огоньком заданий
   B_MM_WispDetect();
   // если расстояние между НПС и ГГ > 5 метров
   if(Npc_GetDistToNpc (self, hero) > 500)
   {
       // НПС бежит к ГГ
       AI_GotoNpc(self,hero);
   }
   else //иначе
   {
       // если прошла секунда
       if(Npc_GetStateTime(self) >= 1)
       {
           // если НПС не видит ГГ
           if(!Npc_CanSeeNpc(self,hero))
           {
               // НПС поворачивается к ГГ
               AI_TurnToNpc(self,hero);
           };
           // увеличение времени вызова монстра
           self.aivar[AIV_SummonTime] = (self.aivar[AIV_SummonTime] + Npc_GetStateTime(self));
           // если время вызова монстра превысило время жизни
           if(self.aivar[AIV_SummonTime] >= MONSTER_SUMMON_TIME)
           {
               // вызванный монстр умирает (жизнь = 0)
               Npc_ChangeAttribute(self,ATR_HITPOINTS,-self.attribute[ATR_HITPOINTS_MAX])
;
           };
           // сброс времени состояния
           Npc_SetStateTime (self, 0);
       };
   };
   // установить для НПС ближайшую свободную точку
   self.wp = Npc_GetNearestWP(self);
   return LOOP_CONTINUE;
};

// Завершение состояния
func void ZS_MM_Rtn_Summoned_End()
{
};


12. Вспомогательные В_ функции для людей.

Все В_ функции расположены в директории ..\AI\Human\B_Human\
// ********************************************************
// Функция переводящая НПС в состояние атаки
// ********************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf              - атакующий НПС (далее - агрессор)
// oth              - атакуемый НПС (далее - жертва)
// attack_reason    - причина атаки
// wait             - время выжидания перед атакой
// ======================================

func void B_Attack(var c_npc slf,var c_npc oth,var int attack_reason,var int wait)
{
   // установить агрессору время задержки перед атакой
   slf.aivar[AIV_WaitBeforeAttack] = wait;
   // если агрессор должен атаковать немедленно
   if(attack_reason == AR_SuddenEnemyInferno)
   {
       // установить агрессору флаг наличия врага
       slf.aivar[AIV_EnemyOverride] = FALSE;
       // послать жертве восприятие нападения
       Npc_SendPassivePerc(slf,PERC_ASSESSFIGHTSOUND,slf,oth);
   };
   // если агрессор находится в состоянии разговора
   if(Npc_IsInState(slf,ZS_Talk))
   {
       // закончить разговор агрессору
       slf.aivar[AIV_INVINCIBLE] = FALSE;
       // закончить разговор жертве
       oth.aivar[AIV_INVINCIBLE] = FALSE;
   };
   // если агрессор находится в состоянии атаки и жертва есть последняя цель агрессора
   if(Npc_IsInState(slf,ZS_Attack)) && (Hlp_GetInstanceID(oth) == slf.aivar[AIV_LASTTARGET])
   {
       // если причина атаки агрессора не убийство жертвы и новая причина атаки приоритетней существующей причины атаки
       if(!C_NpcHasAttackReasonToKill(slf)) && (attack_reason > slf.aivar[AIV_ATTACKREASON])
       {
           // установить агрессору новую причину атаки
           slf.aivar[AIV_ATTACKREASON] = attack_reason;
           // если жертва ГГ
           if(Npc_IsPlayer(oth))
           {
               // установить агрессору причину атаки ГГ
               slf.aivar[AIV_LastPlayerAR] = attack_reason;
           };
       };
   }
   else // иначе
   {
       // установить агрессору новую причину атаки
       slf.aivar[AIV_ATTACKREASON] = attack_reason;
       // если жертва ГГ
       if (Npc_IsPlayer(oth))
       {
           // установить агрессору причину атаки ГГ
           slf.aivar[AIV_LastPlayerAR] = attack_reason;
       };
   };
   // если агрессор атакует убийцу овец
   if(slf.aivar[AIV_ATTACKREASON] == AR_SheepKiller)
   {
       // запоминается новость, что жертва убила овцу
       B_MemorizePlayerCrime(slf,oth,CRIME_SHEEPKILLER);
   };
   // если причина атаки вызвана повреждением агрессора или является реакцией на обнаженное оружие
   if(slf.aivar[AIV_ATTACKREASON] == AR_ReactToDamage) || (slf.aivar[AIV_ATTACKREASON] == AR_ReactToWeapon)
   {
       // если агрессор имеет дело не с "крутым парнем" и (жертва не ГГ и не друг агрессору)
       if(!C_NpcIsToughGuy(slf)) && (!(Npc_IsPlayer(oth) && (slf.npctype == NPCTYPE_FRIEND)))
       {
           // запоминается новость, что ГГ сражался с НПС
           B_MemorizePlayerCrime(slf,oth,CRIME_ATTACK);
       };
   };
   // если причина атаки воровство или использование МОВа
   if(slf.aivar[AIV_ATTACKREASON] == AR_Theft) || (slf.aivar[AIV_ATTACKREASON] == AR_UseMob)
   {
       // запоминается новость, что ГГ воровал
       B_MemorizePlayerCrime(slf,oth,CRIME_THEFT);
   };
   // если причина атаки убийство человека
   if(slf.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)
   {
       // запоминается новость, что ГГ совершил убийство
       B_MemorizePlayerCrime(slf,oth,CRIME_MURDER);
   };
   // если агрессор уже в состоянии атаки
   if(Npc_IsInState(slf,ZS_Attack))
   {
       return;
   };
   // если причина атаки убийство
   if (slf.aivar[AIV_ATTACKREASON] == AR_KILL)
   {
       // установить враждебное отношение агрессора к ГГ
       B_SetAttitude(slf,ATT_HOSTILE);
   };
   // если ГГ жертва
   if(Npc_IsPlayer(oth))
   {
       // параметр последнего сражения с ГГ - прервано
       slf.aivar[AIV_LastFightAgainstPlayer] = FIGHT_CANCEL;
       // последнее сражение не комментировалось
       slf.aivar[AIV_LastFightComment] = FALSE;
   };
   // если агрессор не в состоянии разговора
   if(!Npc_IsInState(slf,ZS_Talk))
   {
       // очистить очередь AI состояний агрессора
       Npc_ClearAIQueue(slf);
   };
   // запретить агрессору восприятия
   B_ClearPerceptions(slf);
   // установить в качестве цели жертву
   Npc_SetTarget(slf,oth);
   // если агрессор лежит
   if(C_BodyStateContains(slf,BS_LIE))
   {
       // переход в состояние атаки с завершением предыдущего состояния
       AI_StartState(slf,ZS_Attack,1,"");
   }
   else // иначе
   {
       // немедленный переход в состояние атаки
       AI_StartState(slf,ZS_Attack,0,"");
   };
   return;
};

// **************************************
// Функция вызова охраны
// --------------------------------------
// self - НПС агрессор, other - НПС жертва
// **************************************

// Функция проверки, может ли НПС вызвать охрану
// ===========================================================
// Аргумент: slf - НПС вызывающий охрану
// -----------------------------------------------------------
// Возвращает: TRUE - охрана может быть вызвана, иначе - FALSE.
// ===========================================================
func int C_WantToCallGuards(var c_npc slf)
{
   // если агрессор не является членом партии ГГ
   if(self.aivar[AIV_PARTYMEMBER] == FALSE)
   {
       // если НПС принадлежит к
       if(slf.guild == GIL_PAL)    // паладинам
       || (slf.guild == GIL_MIL)   // или милиции
       || (slf.guild == GIL_VLK)   // или горожанам
       || (slf.guild == GIL_SLD)   // или наемникам
       || (slf.guild == GIL_BAU)   // или крестьянам
       {
           // охрана может быть вызвана
           return TRUE;
       };
   };
   // охрану вызвать нельзя
   return FALSE;
};

// Вызов охраны
func void B_CallGuards()
{
   // если агрессор не может вызвать охрану
   if(!C_WantToCallGuards(self))
   {
       return;
   };
   // если причина атаки
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToKill)    // вызвана охрана по факту убийства
   || (self.aivar[AIV_ATTACKREASON] == AR_GuardStopsFight)     // или охрана прекратила атаковать преступника
   || (self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToThief)  // или охрана вызвана по факту воровства
   || (self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToRoom)   // или охрана вызвана для защиты помещения
   {
       // агрессор говорит жертве (SVM фраза "ТРЕВОГА!")
       B_Say_Overlay(self,other,"$ALARM");
       return;
   };
   // если причина атаки - нападение на врага или убийство человека
   if(self.aivar[AIV_ATTACKREASON] == AR_GuildEnemy) || (self.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)
   {
       // если агрессор милиционер или паладин или наемник
       if(self.guild == GIL_MIL) || (self.guild == GIL_PAL) || (self.guild == GIL_SLD)
       {
           // агрессор говорит жертве (SVM фраза "ТРЕВОГА!")
           B_Say_Overlay(self,other,"$ALARM");
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           return;
       };
       // если агрессор имеет дело не с "крутым парнем"
       if(!C_NpcIsToughGuy(self))
       {
           // агрессор говорит жертве (SVM фраза "ОХРАНА!")
           B_Say_Overlay(self,other,"$GUARDS");
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           return;
       };
       return;
   };
   // если охрана ворот атакует незваного гостя
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardStopsIntruder)
   {
       // агрессор говорит жертве (SVM фраза "ТРЕВОГА!")
       B_Say_Overlay(self,other,"$ALARM");
       // агрессор посылает восприятие нападения
       Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
       return;
   };
   // если причина атаки воровство или использование чужого МОВа
   if(self.aivar[AIV_ATTACKREASON] == AR_Theft) || (self.aivar[AIV_ATTACKREASON] == AR_UseMob)
   {
       // если агрессор имеет дело не с "крутым парнем"
       if(!C_NpcIsToughGuy(self))
       {
           // агрессор говорит жертве (SVM фраза "ОХРАНА!")
           B_Say_Overlay(self,other,"$GUARDS");
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
       };
       return;
   };
   // если причина нападения - реакция на обнаженное оружие
   if (self.aivar[AIV_ATTACKREASON] == AR_ReactToWeapon)
   {
       // если агрессор имеет дело не с "крутым парнем"
       if(!C_NpcIsToughGuy(self))
       {
           // агрессор говорит жертве (SVM фраза "ОХРАНА!")
           B_Say_Overlay(self,other,"$GUARDS");
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
       };
       return;
   };
   // если причина нападения - посягательство на чужое помещение
   if(self.aivar[AIV_ATTACKREASON] == AR_ClearRoom)
   {
       // если агрессор милиционер или паладин или наемник
       if(self.guild == GIL_MIL) || (self.guild == GIL_PAL) || (self.guild == GIL_SLD)
       {
           // агрессор говорит жертве (SVM фраза "ТРЕВОГА!")
           B_Say_Overlay(self,other,"$ALARM");
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           return;
       };
       // если агрессор имеет дело не с "крутым парнем"
       if(!C_NpcIsToughGuy(self))
       {
           // агрессор говорит жертве (SVM фраза "ОХРАНА!")
           B_Say_Overlay(self,other,"$GUARDS");
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           return;
       };
       return;
   };
   return;
};

// *************************************************************************
// Обработчик состояния комментирования подставной гильдии (переодетого ГГ)
// -------------------------------------------------------------------------
// self - НПС источник информации, other - НПС приемник информации (обычно ГГ)
// *************************************************************************

func void ZS_CommentFakeGuild()
{
   // разрешить минимальный набор восприятий
   Perception_Set_Minimal();
   // НПС встает
   AI_StandUp(self);
   // если НПС не сидит
   if(!C_BodystateContains(self,BS_SIT))
   {
       // НПС поворачивается к ГГ
       B_TurnToNpc(self,other);
   };
   // если ГГ не сидит
   if(!C_BodystateContains(other,BS_SIT))
   {
       // ГГ поворачивается к НПС
       B_TurnToNpc(other,self);
       // если расстояние между ГГ и НПС < 80 см
       if(Npc_GetDistToNpc(other,self) < 80)
       {
           // ГГ делает шаг назад
           AI_Dodge(other);
       };
   };
   // если на ГГ одеты доспехи
   if(Npc_HasEquippedArmor(other) == TRUE)
   {
       var c_item itm;
       // получить ссылку на доспехи
       itm = Npc_GetEquippedArmor(other);
       // если НПС милиционер или паладин
       if(self.guild == GIL_MIL || self.guild == GIL_PAL)
       {
           // НПС говорит ГГ (SVM фраза "Ты солдат! Одевайся соответственно.")
           B_Say (self,other, "$ADDON_WRONGARMOR_MIL");
       }
       // иначе, если НПС маг послушник или маг огня
       else if(self.guild == GIL_NOV || self.guild == GIL_KDF)
       {
           // НПС говорит ГГ (SVM фраза "Эта одежда не соответствует нашему ордену. Переоденься.")
           B_Say(self,other,"$ADDON_WRONGARMOR_KDF");
       }
       // иначе, если НПС наемник или охотник на драконов
       else if(self.guild == GIL_SLD || self.guild == GIL_DJG)
       {
           // НПС говорит ГГ (SVM фраза "В каком виде ты тут бродишь? Одень что-то приличное.")
           B_Say(self,other,"$ADDON_WRONGARMOR_SLD");
       }
       else // иначе (другие гильдии)
       {
           // НПС говорит ГГ (SVM фраза "Это не твоя одежда. Я с тобой не буду говорить.")
           B_Say(self,other,"$ADDON_WRONGARMOR");
       };
   }
   else // иначе (ГГ без доспехов)
   {
       // если НПС бандит
       if(self.guild == GIL_BDT)
       {
           // НПС говорит ГГ (SVM фраза "Да на тебе даже кольчуги нет. Проваливай!")
           B_Say(self,other,"$ADDON_NOARMOR_BDT");
       };
   };
};

// **************************************
// Функция подготовки НПС боеприпасов
// **************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf - экипируемый НПС
// ======================================

func void B_CreateAmmo(var c_npc slf)
{
   var c_item rangedWeapon;
   // если НПС находится в боевом режиме с оружием дальнего радиуса поражения
   if(Npc_IsInFightMode(slf,FMODE_FAR))
   {
       // получить оружие, которое НПС держит в руках
       rangedWeapon = Npc_GetReadiedWeapon(slf);
   }
   // иначе, если НПС экипирован оружием дальнего радиуса поражения
   else if(Npc_HasEquippedRangedWeapon(slf))
   {
       // получить оружие дальнего радиуса поражения, которым экипирован НПС
       rangedWeapon = Npc_GetEquippedRangedWeapon(slf);
   }
   else // иначе
   {
       return;
   };
   // если боезапас - Стрела
   if(rangedWeapon.munition == ItRw_Arrow)
   {
       // если у НПС < 10 стрел
       if(Npc_HasItems(slf,ItRw_Arrow) < 10)
       {
           // создать в инвентаре НПС 10 стрел
           CreateInvItems(slf,ItRw_Arrow,10);
       };
   }
   // иначе, если боезапас - Болт
   else if(rangedWeapon.munition == ItRw_Bolt)
   {
       // если у НПС < 10 болтов
       if(Npc_HasItems(slf,ItRw_Bolt) < 10)
       {
           // создать в инвентаре НПС 10 болтов
           CreateInvItems(slf,ItRw_Bolt,10);
       };
   };
};

// **************************************
// Функция добивания НПС
// **************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf  - НПС агрессор
// oth  - НПС жертва
// ======================================

func void B_FinishingMove(var c_npc slf,var c_npc oth)
{
   // если агрессор не находится в режиме сражения с оружием ближнего радиуса поражения
   if(!Npc_IsInFightMode(slf,FMODE_MELEE))
   {
       // агрессор ставится в режим сражения с оружием Ржавый меч
       Npc_SetToFightMode(slf,ItMw_1h_MISC_Sword);
   };
   // агрессор добивает жертву
   AI_FinishingMove (slf, oth);
};

// ********************************
// НПС перестает смотреть на цель
// ********************************

// ================================
// Аргументы:
// --------------------------------
// slf  - смотрящий НПС
// ================================

func void B_StopLookAt(var c_npc slf)
{
   var c_npc target;
   // получить цель, на которую смотрит НПС
   target = Npc_GetLookAtTarget(slf);
   // если цель существует
   if(Hlp_IsValidNpc(target))
   {
       // НПС перестает смотреть на цель
       AI_StopLookAt (slf);
   };
};

// ***************************
// НПС смотрит на другого НПС
// ***************************

// ===========================
// Аргументы:
// ---------------------------
// slf  - смотрящий НПС
// oth  - НПС цель
// ===========================

func void B_LookAtNpc(var c_npc slf,var c_npc oth)
{
   // НПС перестает смотреть на старую цель
   B_StopLookAt(slf);
   // НПС смотрит на новую цель
   AI_LookAtNpc(slf,oth);
};

// **************************************
// НПС поворачивается к другому НПС
// **************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf  - поворачивающийся НПС
// oth  - НПС цель
// ======================================

func void B_TurnToNpc(var c_npc slf,var c_npc oth)
{
   // НПС поворачивается к цели
   AI_TurnToNpc(slf,oth);
};

// *********************************
// Перевод НПС в исходное состояние
// *********************************

// ================================
// Аргументы:
// --------------------------------
// slf  - НПС, переводимый в исходное состояние
// ================================

func void B_ResetAll(var c_npc slf)
{
   // НПС перестает смотреть на цель
   B_StopLookAt(slf);
   // НПС прячет оружие
   AI_RemoveWeapon(slf);
};

// *******************************
// НПС говорит SVM фразу
// *******************************

// ===============================
// Аргументы:
// -------------------------------
// slf  - говорящий НПС
// oth  - слушающий НПС
// text - имя переменной класса C_SVM, которая содержит произносимую фразу (должно начинаться с символа $)
// ===============================

func void B_Say(var c_npc slf,var c_npc oth,var string text)
{
   // НПС говорит другому НПС SVM (Standart Voice Module) фразу
   AI_OutputSVM(slf,oth,text);
};

// ******************************
// Комментарий завершенной атаки
// ------------------------------
// self - агрессор, other - жертва
// ******************************

func void B_Say_AttackEnd()
{
   // если агрессор атаковал врага или монстра, убившего человека
   if(self.aivar[AIV_ATTACKREASON] == AR_GuildEnemy) || (self.aivar[AIV_ATTACKREASON] == AR_MonsterMurderedHuman)
   {
       // если жертва человек
       if(other.guild < GIL_SEPERATOR_HUM)
       {
           // если жертва не убита
           if(!Npc_IsDead(other))
           {
               // если последняя атакованная цель и есть жертва
               if(self.aivar[AIV_LASTTARGET] == Hlp_GetInstanceID(other))
               {
                   // агрессор говорит жертве (SVM фраза) "Умри, подлец!"
                   B_Say(self,other,"$KILLENEMY");
               }
               else // иначе
               {
                   // агрессор говорит жертве (SVM фраза) "Да, убей эту свинью!"
                   B_Say(self,other,"$GOODKILL");
               };
           }
           else // иначе (жертва мертва)
           {
               // агрессор говорит жертве (SVM фраза) "Ты это заслужил, подлец!"
               B_Say(self,other,"$ENEMYKILLED");
           };
       }
       else // иначе (жертва монстр)
       {
           // если агрессор член партии ГГ
           if(self.aivar[AIV_PARTYMEMBER] == TRUE)
           {
               var int rnd;
               rnd = Hlp_Random(100);
               // если вероятность 0.85 и жертва не дракон
               if(rnd > 15) && (other.guild != GIL_DRAGON)
               {
                   return;
               };
           };
           // если жертва убита не ГГ
           if(other.aivar[AIV_KilledByPlayer] == FALSE)
           {
               // если агрессор имеет номер голоса 9
               if(self.voice == 9)
               {
                   var int random;
                   random = Hlp_Random(2);
                   // если вероятность 0.5
                   if(random == 0)
                   {
                       // агрессор говорит жертве (SVM фраза) "С тобой покончено, скотина!"
                       B_Say(self,other,"$ADDON_MONSTERKILLED");
                   }
                   else
                   {
                       // агрессор говорит жертве (SVM фраза) "На ОДНУ тварь меньше."
                       B_Say(self,other,"$ADDON_MONSTERKILLED2");
                   };
               }
               // иначе, если агрессор имеет номер голоса 12
               else if(self.voice == 12)
               {
                   // если агрессор Горн
                   if((Hlp_GetInstanceID(self) == Hlp_GetInstanceID(GornOW))           // в Рудниковой долине
                   || (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(GornDJG))          // охотник на драконов
                   || (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(GornNW_vor_DJG))   // охотник на драконов в лагере наемников
                   || (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(GornNW_nach_DJG))  // охотник на драконов в 6 главе
                   || (Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Gorn_DI)))         // на острове драконов
                   {
                       var int randy;
                       randy = Hlp_Random(2);
                       // если вероятность 0.5
                       if(randy == 0)
                       {
                           // агрессор говорит жертве (SVM фраза) "С тобой покончено, скотина!"
                           B_Say(self,other,"$ADDON_MONSTERKILLED");
                       }
                       else
                       {
                           // агрессор говорит жертве (SVM фраза) "Одной тварью меньше!"
                           B_Say (self,other,"$MONSTERKILLED");
                       };
                   }
                   else
                   {
                       // агрессор говорит жертве (SVM фраза) "С тобой покончено, скотина!"
                       B_Say(self,other,"$ADDON_MONSTERKILLED");
                   };
               }
               else // другие голоса
               {
                   // агрессор говорит жертве (SVM фраза) "Одной тварью меньше!"
                   B_Say(self,other,"$MONSTERKILLED");
               };
           }
           else // иначе (жертва убита ГГ)
           {
               // ГГ говорит жертве (SVM фраза) "Хорошая работа - одной тварью меньше!"
               B_Say(self,other,"$GOODMONSTERKILL");
           };
       };
       return;
   };
   // если агрессор атаковал вора
   if(self.aivar[AIV_ATTACKREASON] == AR_Theft)
   {
       // агрессор говорит жертве (SVM фраза) "И не пытайся больше обокрасть меня!"
       B_Say(self,other,"$THIEFDOWN");
       return;
   };
   // агрессор атаковал ГГ, вскрывшего его сундук
   if(self.aivar[AIV_ATTACKREASON] == AR_UseMob)
   {
       // агрессор говорит жертве (SVM фраза) "В следующий раз не советую тебе шарить по чужим вещам!"
       B_Say(self,other,"$RUMFUMMLERDOWN");
       return;
   };
   // если агрессор атаковал убийцу овец
   if (self.aivar[AIV_ATTACKREASON] == AR_SheepKiller)
   {
       // если жертва человек
       if(other.guild < GIL_SEPERATOR_HUM)
       {
           // агрессор говорит жертве (SVM фраза) "Никогда больше не делай этого! Это же наши овцы!"
           B_Say(self,other,"$SHEEPATTACKERDOWN");
       }
       else // иначе (жертва монстр)
       {
           // агрессор говорит жертве (SVM фраза) "Одной тварью меньше!"
           B_Say(self,other,"$MONSTERKILLED");
       };
       return;
   };
   // если агрессор атаковал человека, убившего другого человека
   if(self.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)
   {
       // если жертва не убита
       if(!Npc_IsDead(other))
       {
           // если последняя атакованная цель и есть жертва
           if(self.aivar[AIV_LASTTARGET] == Hlp_GetInstanceID(other))
           {
               // агрессор говорит жертве (SVM фраза) "Умри, убийца!"
               B_Say(self,other,"$KILLMURDERER");
           }
           else // иначе
           {
               // агрессор говорит жертве (SVM фраза) "Да, убей эту свинью!"
               B_Say(self,other,"$GOODKILL");
           };
       }
       else // иначе (жертва убита)
       {
           // агрессор говорит жертве (SVM фраза) "Ты это заслужил, подлец!"
           B_Say(self,other,"$ENEMYKILLED");
       };
       return;
   };
   // если агрессор помогает убить монстра
   if(self.aivar[AIV_ATTACKREASON] == AR_MonsterVsHuman)
   {
       // если голос агрессора 9 или 12
       if(self.voice == 9) || (self.voice == 12)
       {
           // агрессор говорит жертве (SVM фраза) "С тобой покончено, скотина!"
           B_Say(self,other,"$ADDON_MONSTERKILLED");
       }
       else // иначе (другие голоса)
       {
           // агрессор говорит жертве (SVM фраза) "Одной тварью меньше!"
           B_Say(self,other,"$MONSTERKILLED");
       };
       return;
   };
   // если охрана ворот атакует монстра
   if(self.aivar[AIV_ATTACKREASON] == AR_MonsterCloseToGate)
   {
       // агрессор говорит жертве (SVM фраза) "Тупоголовая скотина!"
       B_Say(self,other,"$STUPIDBEASTKILLED");
       return;
   };
   // если агрессор реагировал на повреждение (на него напали первым)
   if(self.aivar[AIV_ATTACKREASON] == AR_ReactToDamage)
   {
       // агрессор говорит жертве (SVM фраза) "Больше никогда не спорь со мной!"
       B_Say(self,other,"$NEVERHITMEAGAIN");
       return;
   };
   // если агрессор реагировал на обнаженное оружие
   if(self.aivar[AIV_ATTACKREASON] == AR_ReactToWeapon)
   {
       // агрессор говорит жертве (SVM фраза) "Это будет тебе уроком!"
       B_Say(self,other,"$YOUBETTERSHOULDHAVELISTENED");
       return;
   };
   // если агрессор реагировал на вход в его помещение или вызвана охраны для защиты помещения
   if(self.aivar[AIV_ATTACKREASON] == AR_ClearRoom) || (self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToRoom)
   {
       // если ГГ находится в помещении, принадлежащем агрессору
       if(C_NpcIsBotheredByPlayerRoomGuild(self))
       {
           // агрессор говорит жертве (SVM фраза) "А теперь убирайся отсюда!"
           B_Say(self,other,"$GETUPANDBEGONE");
       }
       else // иначе (вне помещения)
       {
           // агрессор говорит жертве (SVM фраза) "И чтоб я больше тебя здесь не видел!"
           B_Say(self,other,"$NEVERENTERROOMAGAIN");
       };
       return;
   };
   // если причина атаки - ГГ в помещении с ограниченным доступом
   if(self.aivar[AIV_ATTACKREASON] == AR_LeftPortalRoom)
   {
       // агрессор говорит жертве (SVM фраза) "И чтоб я больше тебя здесь не видел!"
       B_Say(self,other,"$NEVERENTERROOMAGAIN");
       return;
   };
   // если охрана ворот атакует незваного гостя
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardStopsIntruder)
   {
       // агрессор говорит жертве (SVM фраза) "Умри, подлец!"
       B_Say(self,other,"$KILLENEMY");
       return;
   };
   // если охрана прекратила атаковать преступника
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardStopsFight)
   {
       // если жертва наемник или охотник на драконов
       if(other.guild == GIL_SLD) || (other.guild == GIL_DJG)
       {
           // молчат
       }
       else // иначе
       {
           // агрессор говорит жертве (SVM фраза) "Борьбы здесь не будет, ясно!? Заруби это себе на носу!"
           B_Say(self,other,"$THEREISNOFIGHTINGHERE");
       };
       return;
   };
   // если вызвана охрана по факту воровства
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToThief)
   {
       // агрессор говорит жертве (SVM фраза) "В следующий раз не советую тебе шарить по чужим вещам!"
       B_Say(self,other,"$RUMFUMMLERDOWN");
       return;
   };
   return;
};

// ********************************
// Комментарий причины атаки
// --------------------------------
// self - агрессор, other - жертва
// ********************************

func void B_Say_AttackReason()
{
   // если гильдии агрессора и жертвы не враждебны и жертва враждебна агрессору
   if(Wld_GetGuildAttitude(self.guild,other.guild) != ATT_HOSTILE) && (Npc_GetAttitude(self,other) == ATT_HOSTILE)
   {
       // если причина атаки - убийство ГГ НПС
       if(self.aivar[AIV_ATTACKREASON] == AR_KILL)
       {
           return;
       };
       // агрессор говорит жертве (SVM фраза) "Я тебя сейчас убью!"
       B_Say_Overlay(self,other,"$IGETYOUSTILL");
       return;
   };
   // если жертва враг гильдии агрессора
   if(self.aivar[AIV_ATTACKREASON] == AR_GuildEnemy)
   {
       // если агрессор член партии ГГ
       if(self.aivar[AIV_PARTYMEMBER] == TRUE)
       {
           var int rnd;
           rnd = Hlp_Random(100);
           // если вероятность 0.85
           if(rnd > 15)
           {
               return;
           };
       };
       // если жертва человек
       if(other.guild < GIL_SEPERATOR_HUM)
       {
           // если ГГ переодетый бандит
           if(C_PlayerIsFakeBandit(self,other) == TRUE)
           {
               // агрессор говорит жертве (SVM фраза) "Умри, грязный бандюга!"
               B_Say_Overlay(self,other,"$ADDON_DIEBANDIT");
               // агрессор посылает восприятие нападения
               Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           }
           // иначе, если ГГ переодетый пират
           else if(C_PlayerIsFakePirate(self,other) == TRUE)
           {
               // агрессор говорит жертве (SVM фраза) "Я тебя прикончу, Пират!"
               B_Say_Overlay (self,other, "$ADDON_DIRTYPIRATE");
               // агрессор посылает восприятие нападения
               Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           }
           else // иначе
           {
               // агрессор говорит жертве (SVM фраза) "Теперь твоя очередь!"
               B_Say_Overlay(self,other,"$DIEENEMY");
               // агрессор посылает восприятие нападения
               Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
           };
       }
       else // иначе (жертва монстр)
       {
           // если голос агрессора 9
           if(self.voice == 9)
           {
               var int random;
               random = Hlp_Random(3);
               if(random < 1)
               {
                   // агрессор говорит жертве (SVM фраза) "Тут снова крутится одна из этих тварей!"
                   B_Say_Overlay(self,other,"$DIEMONSTER");
               }
               else if(random == 1)
               {
                   // агрессор говорит жертве (SVM фраза) "Снова одна из этих тварей!"
                   B_Say_Overlay(self,other,"$ADDON_DIEMONSTER");
               }
               else
               {
                   // агрессор говорит жертве (SVM фраза) "Вот опять одна из тварюг!"
                   B_Say_Overlay(self,other,"$ADDON_DIEMONSTER2");
               };
           }
           else // иначе (другие голоса)
           {
               // агрессор говорит жертве (SVM фраза) "Тут снова крутится одна из этих тварей!"
               B_Say_Overlay(self,other,"$DIEMONSTER");
           };
           // агрессор посылает восприятие нападения
           Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
       };
       return;
   };
   // если агрессор атакует вора
   if(self.aivar[AIV_ATTACKREASON] == AR_Theft)
   {
       // агрессор говорит жертве (SVM фраза) "Погоди у меня, гадкий воришка!"
       B_Say_Overlay(self,other,"$DIRTYTHIEF");
       return;
   };
   // если агрессор атакует грабителя сундуков
   if(self.aivar[AIV_ATTACKREASON] == AR_UseMob)
   {
       // агрессор говорит жертве (SVM фраза) "Руки прочь!"
       B_Say_Overlay(self,other,"$HANDSOFF");
       return;
   };
   // если агрессор атакует убийцу овец
   if(self.aivar[AIV_ATTACKREASON] == AR_SheepKiller)
   {
       // если жертва человек
       if(other.guild < GIL_SEPERATOR_HUM)
       {
           // агрессор говорит жертве (SVM фраза) "Этот подлец убивает наших овец!"
           B_Say_Overlay(self,other,"$SHEEPKILLER");
       }
       else // иначе (жертва монстр)
       {
           // агрессор говорит жертве (SVM фраза) "Эта проклятая тварь сжирает наших овец!"
           B_Say_Overlay(self,other,"$SHEEPKILLERMONSTER");
       };
       return;
   };

   // если агрессор атакует человека, убившего человека
   if(self.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)
   {
       // агрессор говорит жертве (SVM фраза) "Убийца!"
       B_Say_Overlay(self,other,"$YOUMURDERER");
       // агрессор посылает восприятие нападения
       Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
       return;
   };
   // если агрессор атакует монстра, убившего человека
   if(self.aivar[AIV_ATTACKREASON] == AR_MonsterMurderedHuman)
   {
       // нет комментариев
       return;
   };
   // если агрессор помогает убить монстра
   if(self.aivar[AIV_ATTACKREASON] == AR_MonsterVsHuman)
   {
       // агрессор говорит жертве (SVM фраза) "Тут снова крутится одна из этих тварей!"
       B_Say_Overlay(self,other,"$DIEMONSTER");
       // агрессор посылает восприятие нападения
       Npc_SendPassivePerc(self,PERC_ASSESSFIGHTSOUND,self,other);
       return;
   };
   // если охрана ворот атакует монстра
   if (self.aivar[AIV_ATTACKREASON] == AR_MonsterCloseToGate)
   {
       // агрессор говорит жертве (SVM фраза) "Тут не пройдет ни одна тварь!"
       B_Say_Overlay(self,other,"$DIESTUPIDBEAST");
       return;
   };
   // если на агрессора напали
   if(self.aivar[AIV_ATTACKREASON] == AR_ReactToDamage)
   {
       // напавший монстр
       if(other.guild > GIL_SEPERATOR_HUM)
       {
           // агрессор говорит жертве (SVM фраза) "Ты получил, что хотел!"
           B_Say_Overlay(self,other,"$YOUASKEDFORIT");
           return;
       }
       else // иначе (человек)
       {
           // агрессор говорит жертве (SVM фраза) "Ты у меня дождешься, подлец!"
           B_Say_Overlay(self,other,"$YOUDAREHITME");
           return;
       };
   };
   // если агрессор среагировал нп обнаженное оружие
   if(self.aivar[AIV_ATTACKREASON] == AR_ReactToWeapon)
   {
       // агрессор говорит жертве (SVM фраза) "Ты получил, что хотел!"
       B_Say_Overlay(self,other,"$YOUASKEDFORIT");
       return;
   };
   // если агрессор реагировал на вход в его помещение
   if(self.aivar[AIV_ATTACKREASON] == AR_ClearRoom)
   {
       // агрессор говорит жертве (SVM фраза) "Тогда я должен тебя поколотить!"
       B_Say_Overlay(self,other,"$THENIBEATYOUOUTOFHERE");
       return;
   };
   // если причина атаки - ГГ в помещении с ограниченным доступом
   if(self.aivar[AIV_ATTACKREASON] == AR_LeftPortalRoom)
   {
       // агрессор говорит жертве (SVM фраза) "Чего это ты там ищешь, ей!"
       B_Say_Overlay(self,other,"$WHATDIDYOUDOINTHERE");
       return;
   };
   // если охрана прекратила атаковать преступника
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardStopsFight)
   {
       // агрессор говорит жертве (SVM фраза) "Вы хотите с этим покончить по-хорошему?!"
       B_Say_Overlay(self,other,"$WILLYOUSTOPFIGHTING");
       return;
   };
   // если вызвана охрана по факту воровства
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToThief)
   {
       return;
   };
   // если вызвана охраны для защиты помещения
   if(self.aivar[AIV_ATTACKREASON] == AR_GuardCalledToRoom)
   {
       return;
   };
   return;
};

// **************************************************************
// НПС slf называет НПС oth сумму денежного выражения goldAmount
// **************************************************************

func void B_Say_Gold(var c_npc slf,var c_npc oth,var int goldAmount)
{
   if(goldAmount == 1000)  { B_Say(slf,oth,"$GOLD_1000");  };
   if(goldAmount == 950)   { B_Say(slf,oth,"$GOLD_950");   };
   if(goldAmount == 900)   { B_Say(slf,oth,"$GOLD_900");   };
   if(goldAmount == 850)   { B_Say(slf,oth,"$GOLD_850");   };
   if(goldAmount == 800)   { B_Say(slf,oth,"$GOLD_800");   };
   if(goldAmount == 750)   { B_Say(slf,oth,"$GOLD_750");   };
   if(goldAmount == 700)   { B_Say(slf,oth,"$GOLD_700");   };
   if(goldAmount == 650)   { B_Say(slf,oth,"$GOLD_650");   };
   if(goldAmount == 600)   { B_Say(slf,oth,"$GOLD_600");   };
   if(goldAmount == 550)   { B_Say(slf,oth,"$GOLD_550");   };
   if(goldAmount == 500)   { B_Say(slf,oth,"$GOLD_500");   };
   if(goldAmount == 450)   { B_Say(slf,oth,"$GOLD_450");   };
   if(goldAmount == 400)   { B_Say(slf,oth,"$GOLD_400");   };
   if(goldAmount == 350)   { B_Say(slf,oth,"$GOLD_350");   };
   if(goldAmount == 300)   { B_Say(slf,oth,"$GOLD_300");   };
   if(goldAmount == 250)   { B_Say(slf,oth,"$GOLD_250");   };
   if(goldAmount == 200)   { B_Say(slf,oth,"$GOLD_200");   };
   if(goldAmount == 150)   { B_Say(slf,oth,"$GOLD_150");   };
   if(goldAmount == 100)   { B_Say(slf,oth,"$GOLD_100");   };
   if(goldAmount == 90)    { B_Say(slf,oth,"$GOLD_90");    };
   if(goldAmount == 80)    { B_Say(slf,oth,"$GOLD_80");    };
   if(goldAmount == 70)    { B_Say(slf,oth,"$GOLD_70");    };
   if(goldAmount == 60)    { B_Say(slf,oth,"$GOLD_60");    };
   if(goldAmount == 50)    { B_Say(slf,oth,"$GOLD_50");    };
   if(goldAmount == 40)    { B_Say(slf,oth,"$GOLD_40");    };
   if(goldAmount == 30)    { B_Say(slf,oth,"$GOLD_30");    };
   if(goldAmount == 20)    { B_Say(slf,oth,"$GOLD_20");    };
   if(goldAmount == 10)    { B_Say(slf,oth,"$GOLD_10");    };
};

// *******************************
// НПС slf преветствует НПС oth
// *******************************

func void B_Say_GuildGreetings(var c_npc slf,var c_npc oth)
{
   // если милиция приветствует милицию или паладина
   if(slf.guild == GIL_MIL) && ((oth.guild == GIL_MIL) || (oth.guild == GIL_PAL))
   {
       // звучит SVM фраза "За короля!"
       B_Say_Overlay(slf,oth,"$MILGREETINGS");
       return;
   };
   // если паладин приветствует паладина или милицию или магов огня
   if(slf.guild == GIL_PAL) && ((oth.guild == GIL_PAL) || (oth.guild == GIL_MIL) || (oth.guild == GIL_KDF))
   {
       // звучит SVM фраза "За Инноса!"
       B_Say_Overlay (slf,oth,"$PALGREETINGS");
       return;
   };
   // если маг огня приветствует паладина или мага послушника или мага огня
   if(slf.guild == GIL_KDF) && ((oth.guild == GIL_PAL) || (oth.guild == GIL_NOV) || (oth.guild == GIL_KDF))
   {
       // звучит SVM фраза "За Инноса!"
       B_Say_Overlay (slf,oth,"$PALGREETINGS");
       return;
   };
   // если приветствующий Барток и он сообщал об одиноком орке перед Хоринисом
   if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Bartok)) && (Bartok_OrkGesagt == TRUE)
   {
       // Барток говорит об одиноком орке перед Хоринисом
       B_Bartok_ShitAnOrc();
       return;
   };
   // если приветствующий Кок
   if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Koch))
   {
       // Кок говорит о топоре
       B_Koch_Hackebeil();
       return;
   };
   var int zufall;
   zufall = Hlp_Random(100);
   // с вероятностью 0.1 и если идет дождь
   if((zufall <= 10) && Wld_IsRaining())
   {
       // звучит SVM фраза "Ну и скверная же погода!"
       B_Say_Overlay(slf,oth,"$WEATHER");
       return;

   };
};

// ************************************************
//  Мини реплики НПС
// ************************************************

func void B_Say_Smalltalk()
{
   var int random;
   random = Hlp_Random (120);
   var int Choice;
   Choice = Hlp_Random(1);
   // с вероятностью 0.04
   if(random < 5)
   {
       // НПС произносит SVM фразу "... ты, правда, так думаешь..."
       B_Say(self,self,"$SMALLTALK01");
   }
   // с вероятностью 0.04
   else if(random < 10)
   {
       // НПС произносит SVM фразу "... все возможно..."
       B_Say(self,self,"$SMALLTALK02");
   }
   // с вероятностью 0.04
   else if(random < 15)
   {
       // НПС произносит SVM фразу "... он должен больше знать об этом..."
       B_Say(self,self,"$SMALLTALK03");
   }
   // с вероятностью 0.04
   else if(random < 20)
   {
       // НПС произносит SVM фразу "... как будто у меня и так мало проблем..."
       B_Say(self,self,"$SMALLTALK04");
   }
   // с вероятностью 0.04
   else if(random < 25)
   {
       // НПС произносит SVM фразу "... кто мог рассказать такое..."
       B_Say(self,self,"$SMALLTALK05");
   }
   // с вероятностью 0.04
   else if(random < 30)
   {
       // НПС произносит SVM фразу "... все же я еще очень зол..."
       B_Say(self,self,"$SMALLTALK06");
   }
   // с вероятностью 0.04
   else if(random < 35)
   {
       // НПС произносит SVM фразу "... кое-что болтают..."
       B_Say(self,self,"$SMALLTALK07");
   }
   // с вероятностью 0.04
   else if(random < 40)
   {
       // НПС произносит SVM фразу "... я бы не делал этого..."
       B_Say(self,self,"$SMALLTALK08");
   }
   // с вероятностью 0.04
   else if(random < 45)
   {
       // НПС произносит SVM фразу "... это всего лишь запах..."
       B_Say(self,self,"$SMALLTALK09");
   }
   // с вероятностью 0.04
   else if(random < 50)
   {
       // НПС произносит SVM фразу "... нужно узнать, что они там замышляют..."
       B_Say(self,self,"$SMALLTALK10");
   }
   // с вероятностью 0.04
   else if(random < 55)
   {
       // НПС произносит SVM фразу "... а я тебе что говорил..."
       B_Say(self,self,"$SMALLTALK11");
   }
   // с вероятностью 0.04
   else if(random < 60)
   {
       // НПС произносит SVM фразу "... меня никто не спрашивал..."
       B_Say(self,self,"$SMALLTALK12");
   }
   // с вероятностью 0.04
   else if(random < 65)
   {
       // НПС произносит SVM фразу "... бедный малый может и обидеть кого-нибудь..."
       B_Say(self,self,"$SMALLTALK13");
   }
   // с вероятностью 0.04
   else if(random < 70)
   {
       // НПС произносит SVM фразу "... ничего нового..."
       B_Say(self,self,"$SMALLTALK14");
   }
   // с вероятностью 0.04
   else if(random < 75)
   {
       // НПС произносит SVM фразу "... все ясно..."
       B_Say(self,self,"$SMALLTALK15");
   }
   // с вероятностью 0.04
   else if(random < 80)
   {
       // НПС произносит SVM фразу "... меня не нужно спрашивать..."
       B_Say(self,self,"$SMALLTALK16");
   }
   // с вероятностью 0.04
   else if(random < 85)
   {
       // НПС произносит SVM фразу "... так не может дальше продолжаться..."
       B_Say(self,self,"$SMALLTALK17");
   }
   // с вероятностью 0.04
   else if(random < 90)
   {
       // НПС произносит SVM фразу "... мое мнение ты уже знаешь..."
       B_Say(self,self,"$SMALLTALK18");
   }
   // с вероятностью 0.04
   else if(random < 95)
   {
       // НПС произносит SVM фразу "... я сказал то же самое..."
       B_Say(self,self,"$SMALLTALK19");
   }
   // с вероятностью 0.04
   else if(random < 100)
   {
       // НПС произносит SVM фразу "... вряд ли что-нибудь изменится из-за этого..."
       B_Say(self,self,"$SMALLTALK20");
   }
   // с вероятностью 0.04
   else if(random < 105)
   {
       // НПС произносит SVM фразу "... почему я узнаю об этом только сейчас..."
       B_Say(self,self,"$SMALLTALK21");
   }
   // с вероятностью 0.04
   else if(random < 110)
   {
       // если НПС маг послушник или паладин или маг огня
       if(Npc_GetTrueGuild(self) == GIL_NOV) || (Npc_GetTrueGuild(self) == GIL_PAL) || (Npc_GetTrueGuild(self) == GIL_KDF)
       {
           // с вероятностью 0.5
           if(Choice == 0)
           {
               // НПС произносит SVM фразу "... так написано в священных книгах..."
               B_Say(self,self,"$SMALLTALK28");
           }
           else
           {
               // НПС произносит SVM фразу "... давайте посмотрим, что из этого выйдет..."
               B_Say(self,self,"$SMALLTALK22");
           };
       }
       // если НПС бандит или наемник или охотник на драконов или пират
       else if(Npc_GetTrueGuild(self) == GIL_BDT) || (Npc_GetTrueGuild(self) == GIL_SLD) || (Npc_GetTrueGuild(self) == GIL_DJG)
       ||  (Npc_GetTrueGuild(self) == GIL_PIR)
       {
           // с вероятностью 0.5
           if(Choice == 0)
           {
               // НПС произносит SVM фразу "... он был пьянючий..."
               B_Say(self,self,"$SMALLTALK25");
           }
           else
           {
               // НПС произносит SVM фразу "... давайте посмотрим, что из этого выйдет..."
               B_Say(self,self,"$SMALLTALK22");
           };
       }
       else // остальные гильдии
       {
           // НПС произносит SVM фразу "... давайте посмотрим, что из этого выйдет..."
           B_Say(self,self,"$SMALLTALK22");
       };
   }
   // с вероятностью 0.04
   else if(random < 115)
   {
       // если НПС маг послушник или паладин или маг огня
       if(Npc_GetTrueGuild(self) == GIL_NOV) || (Npc_GetTrueGuild(self) == GIL_PAL) || (Npc_GetTrueGuild(self) == GIL_KDF)
       {
           // с вероятностью 0.5
           if(Choice == 0)
           {
               // НПС произносит SVM фразу "... я действую по приказу Инноса..."
               B_Say(self,self,"$SMALLTALK29");
           }
           else
           {
               // НПС произносит SVM фразу "... некоторые проблемы решаются сами собой..."
               B_Say(self,self,"$SMALLTALK23");
           };
       }
       // если НПС бандит или наемник или охотник на драконов или пират
       else if(Npc_GetTrueGuild(self) == GIL_BDT) || (Npc_GetTrueGuild(self) == GIL_SLD) || (Npc_GetTrueGuild(self) == GIL_DJG)
       || (Npc_GetTrueGuild(self) == GIL_PIR)
       {
           // с вероятностью 0.5
           if(Choice == 0)
           {
               // НПС произносит SVM фразу "... со мной бы этот номер не прошел..."
               B_Say(self,self,"$SMALLTALK26");
           }
           else
           {
               // НПС произносит SVM фразу "... некоторые проблемы решаются сами собой..."
               B_Say(self,self,"$SMALLTALK23");
           };
       }
       else // остальные гильдии
       {
           // НПС произносит SVM фразу "... некоторые проблемы решаются сами собой..."
           B_Say(self,self,"$SMALLTALK23");
       };
   }
   // с вероятностью 0.04
   else if(random <= 120)
   {
       // если НПС маг послушник или паладин или маг огня
       if(Npc_GetTrueGuild(self) == GIL_NOV) || (Npc_GetTrueGuild(self) == GIL_PAL) || (Npc_GetTrueGuild(self) == GIL_KDF)
       {
           // с вероятностью 0.5
           if(Choice == 0)
           {
               // НПС произносит SVM фразу "... никому не позволено нарушать указания богов..."
               B_Say(self,self,"$SMALLTALK30");
           }
           else
           {
               // НПС произносит SVM фразу "... я не могу больше это слушать..."
               B_Say(self,self,"$SMALLTALK24");
           };
       }
       // если НПС бандит или наемник или охотник на драконов или пират
       else if(Npc_GetTrueGuild(self) == GIL_BDT) || (Npc_GetTrueGuild(self) == GIL_SLD) || (Npc_GetTrueGuild(self) == GIL_DJG)
       || (Npc_GetTrueGuild (self) == GIL_PIR)
       {
           // с вероятностью 0.5
           if(Choice == 0)
           {
               // НПС произносит SVM фразу "... все убежали словно зайцы, я остался один..."
               B_Say(self,self,"$SMALLTALK27");
           }
           else
           {
               // НПС произносит SVM фразу "... я не могу больше это слушать..."
               B_Say(self,self,"$SMALLTALK24");
           };
       }
       else // остальные гильдии
       {
           // НПС произносит SVM фразу "... я не могу больше это слушать..."
           B_Say(self,self,"$SMALLTALK24");
       };
   };
};

// ****************************************************************
// НПС slf произносит для НПС oth быструю SVM фразу text
// ----------------------------------------------------------------
// От B_Say() отличается только тем, что следующие AI команды будут
// выполняться не ожидая окончания фразы
// ****************************************************************

func void B_Say_Overlay(var c_npc slf,var c_npc oth,var string text)
{
   AI_OutputSVM_Overlay(slf,oth,text);
};

// *******************************************
// Выбор НПС оружия соответствующего цели
// *******************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf  - НПС агрессор
// oth  - НПС цель
// ======================================

func void B_SelectWeapon(var c_npc slf,var c_npc oth)
{
   // если агрессор падает или плывет или ныряет
   if(C_BodyStateContains(slf,BS_FALL)) || (C_BodyStateContains(slf,BS_SWIM)) || (C_BodyStateContains(slf,BS_DIVE))
   {
       return;
   };
   // если агрессор выбрал для использования магию
   if(B_SelectSpell(slf,oth))
   {
       return;
   };
   // если агрессор находится в режиме сражения магией
   if(Npc_IsInFightMode(slf,FMODE_MAGIC))
   {
       // если агрессор находится в боевом режиме (Примечание: не мой взгляд этот блок лишний, была проверка, что НПС использует магию)
       if(!Npc_IsInFightMode(slf,FMODE_NONE))
       {
           // агрессор прячет оружие
           AI_RemoveWeapon(slf);
       };
       // подготовка оружия ближнего радиуса поражения к бою
       AI_ReadyMeleeWeapon (slf);
       return;
   };
   // если агрессор находится в режиме сражения оружием дальнего радиуса поражения
   if(Npc_IsInFightMode(slf,FMODE_FAR))
   {
       // если расстояние от агрессора до цели > дистанции, ближе которой НПС выбирает оружие ближнего радиуса поражения или агрессор не экипирован оружием ближнего радиуса поражения
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_RANGED_INNER) || (!Npc_HasEquippedMeleeWeapon(slf))
       {
           return;
       };
   };
   // если агрессор находится в режиме сражения оружием ближнего радиуса поражения
   if(Npc_IsInFightMode(slf,FMODE_MELEE))
   {
       // если расстояние от агрессора до цели > дистанции, дальше которой НПС выбирает оружие дальнего радиуса поражения или агрессор не экипирован оружием дальнего радиуса поражения
       if(Npc_GetDistToNpc(slf,oth) <= FIGHT_DIST_RANGED_OUTER) || (!Npc_HasEquippedRangedWeapon(slf))
       {
           return;
       };
   };
   // если агрессор экипирован оружием ближнего радиуса поражения и расстояние от агрессора до цели <= дистанции, дальше которой НПС выбирает оружие дальнего радиуса поражения
   if(Npc_HasEquippedMeleeWeapon(slf)) && (Npc_GetDistToNpc(slf,oth) <= FIGHT_DIST_RANGED_OUTER)
   {
       // если агрессор находится в боевом режиме
       if(!Npc_IsInFightMode(slf,FMODE_NONE))
       {
           // агрессор прячет оружие
           AI_RemoveWeapon(slf);
       };
       // подготовка оружия ближнего радиуса поражения к бою
       AI_ReadyMeleeWeapon (slf);
       return;
   };
   // если агрессор экипирован оружием дальнего радиуса поражения и расстояние от агрессора до цели > дистанции, ближе которой НПС выбирает оружие ближнего радиуса поражения и причина атаки агрессора - убийство жертвы
   if(Npc_HasEquippedRangedWeapon(slf)) && (Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_RANGED_INNER) && (C_NpcHasAttackReasonToKill(slf))
   {
       // если агрессор находится в боевом режиме
       if(!Npc_IsInFightMode(slf,FMODE_NONE))
       {
           // агрессор прячет оружие
           AI_RemoveWeapon(slf);
       };
       // подготовка оружия дальнего радиуса поражения к бою
       AI_ReadyRangedWeapon(slf);
       return;
   };
   // если агрессор не в боевом режиме
   if(Npc_IsInFightMode(slf,FMODE_NONE))
   {
       // если агрессор экипирован оружием ближнего радиуса поражения
       if(Npc_HasEquippedMeleeWeapon(slf))
       {
           // подготовка оружия к бою
           AI_ReadyMeleeWeapon(slf);
           return;
       };
       // если агрессор экипирован оружием дальнего радиуса поражения
       if(Npc_HasEquippedRangedWeapon(slf))
       {
           // подготовка оружия к бою
           AI_ReadyRangedWeapon(slf);
           return;
       };
       // подготовка к бою кулаками
       AI_ReadyMeleeWeapon(slf);
       return;
   };
   return;
};

// *********************************
// Устанавливает отношение НПС к ГГ
// *********************************

// ======================================
// Аргументы:
// --------------------------------------
// slf  - НПС
// att  - отношение
// ======================================

func void B_SetAttitude(var c_npc slf,var int att)
{
   // установить временное отношение НПС к ГГ
   Npc_SetAttitude(slf,att);
   // установить постояное отношение НПС к ГГ
   Npc_SetTempAttitude(slf,att);
};

// ********************************************
// Инициализация переменной other для НПС self
// ********************************************

func void B_ValidateOther()
{
   // если other отсутсвует (загрузка игры)
   if(!Hlp_IsValidNpc(other))
   {
       // other = последней цели НПС
       other = Hlp_GetNpc(self.aivar[AIV_LASTTARGET]);
       // для НПС установить цель other
       Npc_SetTarget(self,other);
       // установить флаг загрузки игры
       self.aivar[AIV_LOADGAME] = TRUE;
   }
   else // иначе (other существует)
   {
       // повторно инициализировать other целью
       Npc_GetTarget(self);
       // сохранить последнюю цель
       self.aivar[AIV_LASTTARGET] = Hlp_GetInstanceID(other);
       // сброс флага загрузки игры
       self.aivar[AIV_LOADGAME] = FALSE;
   };
};

Добавление от 26 марта 2005, 09:56
Криминал ГГ

// *******************************
// Запоминание новостей
// *******************************

// ======================================
// Аргументы:
// --------------------------------------
// slf  - НПС, запоминающий новость
// taeter  - НПС преступник
// crime - криминальная новость
// ======================================

func void B_MemorizePlayerCrime(var c_npc slf,var c_npc taeter,var int crime)
{
   // если преступник не ГГ
   if(!Npc_IsPlayer(taeter))
   {
       return;
   };
   // если НПС черный маг
   if(slf.guild == GIL_DMT)
   {
       return;
   };
   // если новость более значима чем предыдущие новости
   if(crime > B_GetPlayerCrime(slf))
   {
       // удалить предыдущую новость
       B_DeletePetzCrime(slf);
       // добавить новость
       B_AddPetzCrime(slf,crime);
   };
   // если новость одного порядка с предыдущей новостью (Примечание: можно оставить ==, > было ранее)
   if(crime >= B_GetPlayerCrime(slf))
   {
       // НПС запоминает новость
       slf.aivar[AIV_NpcSawPlayerCommit] = crime;
       // НПС запоминает день преступления
       slf.aivar[AIV_NpcSawPlayerCommitDay] = Wld_GetDay();
       // НПС запоминает уровень криминала ГГ
       slf.aivar[AIV_CrimeAbsolutionLevel] = B_GetCurrentAbsolutionLevel(slf);
   };
};

// ************************************************************
// Получить последнюю новость, запомненную НПС о криминале ГГ
// ************************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf  - НПС имеющий новость
// --------------------------------------
// Возвращаемое значение: последняя запомненная новость
// ======================================

func int B_GetPlayerCrime(var c_npc slf)
{
   // если уровень криминала ГГ меньше воровства
   if(slf.aivar[AIV_NpcSawPlayerCommit] <= CRIME_ATTACK)
   {
       // если с момента запоминания новости прошло более двух дней
       if(slf.aivar[AIV_NpcSawPlayerCommitDay] < (Wld_GetDay()-2))
       {
           // криминала не было
           return CRIME_NONE;
       };
   };
   // если уровень криминала, запомненный НПС, < уровня криминала ГГ по локациям
   if(slf.aivar[AIV_CrimeAbsolutionLevel] < B_GetCurrentAbsolutionLevel(slf))
   {
       // криминала не было
       return CRIME_NONE;
   };
   // последний запомненный криминал
   return slf.aivar[AIV_NpcSawPlayerCommit];
};

// **************************************************
// Добавить новость для соответствующей локации
// **************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf - НПС свидетель, проживающий в локации
// crime - криминальная новость
// ======================================

func void B_AddPetzCrime(var c_npc slf,var int crime)
{
   // если НПС из Миненталя
   if(C_NpcBelongsToOldCamp(slf))
   {
       // если убийство
       if(crime == CRIME_MURDER)
       {
           // увеличить счетчик убийств
           PETZCOUNTER_OldCamp_Murder = PETZCOUNTER_OldCamp_Murder + 1;
       };
       // если воровство
       if(crime == CRIME_THEFT)
       {
           // увеличить счетчик воровства
           PETZCOUNTER_OldCamp_Theft = PETZCOUNTER_OldCamp_Theft + 1;
       };
       // если нападение
       if(crime == CRIME_ATTACK)
       {
           // увеличить счетчик нападений
           PETZCOUNTER_OldCamp_Attack = PETZCOUNTER_OldCamp_Attack + 1;
       };
       // если убийство овец
       if(crime == CRIME_SHEEPKILLER)
       {
           // увеличить счетчик убитых овец
           PETZCOUNTER_OldCamp_Sheepkiller = PETZCOUNTER_OldCamp_Sheepkiller + 1;
       };
   };
   // если НПС из Хориниса
   if(C_NpcBelongsToCity(slf))
   {
       if(crime == CRIME_MURDER)
       {
           PETZCOUNTER_City_Murder = PETZCOUNTER_City_Murder + 1;
       };
       if(crime == CRIME_THEFT)
       {
           PETZCOUNTER_City_Theft = PETZCOUNTER_City_Theft + 1;
       };
       if(crime == CRIME_ATTACK)
       {
           PETZCOUNTER_City_Attack = PETZCOUNTER_City_Attack + 1;
       };
       if(crime == CRIME_SHEEPKILLER)
       {
           PETZCOUNTER_City_Sheepkiller = PETZCOUNTER_City_Sheepkiller + 1;
       };
   };
   // если НПС из Монастыря
   if(C_NpcBelongsToMonastery(slf))
   {
       if(crime == CRIME_MURDER)
       {
           PETZCOUNTER_Monastery_Murder = PETZCOUNTER_Monastery_Murder + 1;
       };
       if(crime == CRIME_THEFT)
       {
           PETZCOUNTER_Monastery_Theft = PETZCOUNTER_Monastery_Theft + 1;
       };
       if(crime == CRIME_ATTACK)
       {
           PETZCOUNTER_Monastery_Attack = PETZCOUNTER_Monastery_Attack + 1;
       };
       if(crime == CRIME_SHEEPKILLER)
       {
           PETZCOUNTER_Monastery_Sheepkiller = PETZCOUNTER_Monastery_Sheepkiller + 1;
       };
   };
   // если НПС с Фермы наемников
   if(C_NpcBelongsToFarm(slf))
   {
       if(crime == CRIME_MURDER)
       {
           PETZCOUNTER_Farm_Murder = PETZCOUNTER_Farm_Murder + 1;
       };
       if(crime == CRIME_THEFT)
       {
           PETZCOUNTER_Farm_Theft = PETZCOUNTER_Farm_Theft + 1;
       };
       if(crime == CRIME_ATTACK)
       {
           PETZCOUNTER_Farm_Attack = PETZCOUNTER_Farm_Attack + 1;
       };
       if(crime == CRIME_SHEEPKILLER)
       {
           PETZCOUNTER_Farm_Sheepkiller = PETZCOUNTER_Farm_Sheepkiller + 1;
       };
   };
   // если НПС из Лагеря бандитов
   if(C_NpcBelongsToBL(slf))
   {
       if(crime == CRIME_MURDER)
       {
           PETZCOUNTER_BL_Murder = PETZCOUNTER_BL_Murder + 1;
       };
       if(crime == CRIME_THEFT)
       {
           PETZCOUNTER_BL_Theft = PETZCOUNTER_BL_Theft + 1;
       };
       if(crime == CRIME_ATTACK)
       {
           PETZCOUNTER_BL_Attack = PETZCOUNTER_BL_Attack + 1;
       };
   };
};

// *************************************************************
// Удалить новость для соответствующей локации
// *************************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf - НПС свидетель, проживающий в локации
// ======================================

func void B_DeletePetzCrime(var c_npc slf)
{
   // если НПС из Миненталя
   if(C_NpcBelongsToOldCamp(slf))
   {
       // если последняя запомненная новость - убийство
       if(B_GetPlayerCrime(slf) == CRIME_MURDER)
       {
           // уменьшить счетчик убийств
           PETZCOUNTER_OldCamp_Murder = PETZCOUNTER_OldCamp_Murder - 1;
       };
       // если последняя запомненная новость - воровство
       if(B_GetPlayerCrime(slf) == CRIME_THEFT)
       {
           // уменьшить счетчик воровства
           PETZCOUNTER_OldCamp_Theft = PETZCOUNTER_OldCamp_Theft - 1;
       };
       // если последняя запомненная новость - нападение
       if(B_GetPlayerCrime(slf) == CRIME_ATTACK)
       {
           // уменьшить счетчик нападений
           PETZCOUNTER_OldCamp_Attack = PETZCOUNTER_OldCamp_Attack - 1;
       };
       // если последняя запомненная новость - убийство овец
       if(B_GetPlayerCrime(slf) == CRIME_SHEEPKILLER)
       {
           // уменьшить счетчик убитых овец
           PETZCOUNTER_OldCamp_Sheepkiller = PETZCOUNTER_OldCamp_Sheepkiller - 1;
       };
   };
   // если НПС из Хориниса
   if(C_NpcBelongsToCity(slf))
   {
       if(B_GetPlayerCrime(slf) == CRIME_MURDER)
       {
           PETZCOUNTER_City_Murder = PETZCOUNTER_City_Murder - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_THEFT)
       {
           PETZCOUNTER_City_Theft = PETZCOUNTER_City_Theft - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_ATTACK)
       {
           PETZCOUNTER_City_Attack = PETZCOUNTER_City_Attack - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_SHEEPKILLER)
       {
           PETZCOUNTER_City_Sheepkiller = PETZCOUNTER_City_Sheepkiller - 1;
       };
   };
   // если НПС из Монастыря
   if(C_NpcBelongsToMonastery(slf))
   {
       if(B_GetPlayerCrime(slf) == CRIME_MURDER)
       {
           PETZCOUNTER_Monastery_Murder = PETZCOUNTER_Monastery_Murder - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_THEFT)
       {
           PETZCOUNTER_Monastery_Theft = PETZCOUNTER_Monastery_Theft - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_ATTACK)
       {
           PETZCOUNTER_Monastery_Attack = PETZCOUNTER_Monastery_Attack - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_SHEEPKILLER)
       {
           PETZCOUNTER_Monastery_Sheepkiller = PETZCOUNTER_Monastery_Sheepkiller - 1;
       };
   };
   // если НПС с Фермы наемников
   if(C_NpcBelongsToFarm(slf))
   {
       if(B_GetPlayerCrime(slf) == CRIME_MURDER)
       {
           PETZCOUNTER_Farm_Murder = PETZCOUNTER_Farm_Murder - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_THEFT)
       {
           PETZCOUNTER_Farm_Theft = PETZCOUNTER_Farm_Theft - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_ATTACK)
       {
           PETZCOUNTER_Farm_Attack = PETZCOUNTER_Farm_Attack - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_SHEEPKILLER)
       {
           PETZCOUNTER_Farm_Sheepkiller = PETZCOUNTER_Farm_Sheepkiller - 1;
       };
   };
   // если НПС из Лагеря бандитов
   if(C_NpcBelongsToBL(slf))
   {
       if(B_GetPlayerCrime(slf) == CRIME_MURDER)
       {
           PETZCOUNTER_BL_Murder = PETZCOUNTER_BL_Murder - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_THEFT)
       {
           PETZCOUNTER_BL_Theft = PETZCOUNTER_BL_Theft - 1;
       };
       if(B_GetPlayerCrime(slf) == CRIME_ATTACK)
       {
           PETZCOUNTER_BL_Attack = PETZCOUNTER_BL_Attack - 1;
       };
   };
};

// *************************************************
// Подсчет абсолютного уровня криминала по локациям
// *************************************************

func void B_GrantAbsolution(var int location)
{
   // если Миненталь или все локации
   if(location == LOC_OLDCAMP) || (location == LOC_ALL)
   {
       // абсолютный уровень по локации++
       ABSOLUTIONLEVEL_OldCamp = ABSOLUTIONLEVEL_OldCamp + 1;
       // сброс счетчиков криминала по локации
       PETZCOUNTER_OldCamp_Murder      = 0;
       PETZCOUNTER_OldCamp_Theft       = 0;
       PETZCOUNTER_OldCamp_Attack      = 0;
       PETZCOUNTER_OldCamp_Sheepkiller = 0;
   };
   // если Хоринис или все локации
   if(location == LOC_CITY) || (location == LOC_ALL)
   {
       ABSOLUTIONLEVEL_City = ABSOLUTIONLEVEL_City + 1;
       PETZCOUNTER_City_Murder         = 0;
       PETZCOUNTER_City_Theft          = 0;
       PETZCOUNTER_City_Attack         = 0;
       PETZCOUNTER_City_Sheepkiller    = 0;
   };
   // если Монастырь или все локации
   if(location == LOC_MONASTERY) || (location == LOC_ALL)
   {
       ABSOLUTIONLEVEL_Monastery = ABSOLUTIONLEVEL_Monastery + 1;
       PETZCOUNTER_Monastery_Murder        = 0;
       PETZCOUNTER_Monastery_Theft         = 0;
       PETZCOUNTER_Monastery_Attack        = 0;
       PETZCOUNTER_Monastery_Sheepkiller   = 0;
   };
   // если Ферма наемников или все локации
   if(location == LOC_FARM) || (location == LOC_ALL)
   {
       ABSOLUTIONLEVEL_Farm = ABSOLUTIONLEVEL_Farm + 1;
       PETZCOUNTER_Farm_Murder         = 0;
       PETZCOUNTER_Farm_Theft          = 0;
       PETZCOUNTER_Farm_Attack         = 0;
       PETZCOUNTER_Farm_Sheepkiller    = 0;
   };
   // если Лагерь бандитов
   if(location == LOC_BL)
   {
       ABSOLUTIONLEVEL_BL = ABSOLUTIONLEVEL_BL + 1;
       PETZCOUNTER_BL_Murder           = 0;
       PETZCOUNTER_BL_Theft            = 0;
       PETZCOUNTER_BL_Attack           = 0;
   };
};

// ***********************************************************************
// Получить абсолютный уровень криминала для локации
// ***********************************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf - НПС, проживающий в локации
// --------------------------------------
// Возвращаемое значение - абсолютный уровень криминала
// ======================================

func int B_GetCurrentAbsolutionLevel(var c_npc slf)
{
   // если НПС из Миненталя
   if(C_NpcBelongsToOldCamp(slf))
   {
       return ABSOLUTIONLEVEL_OldCamp;
   };
   // если НПС из Хориниса
   if(C_NpcBelongsToCity(slf))
   {
       return ABSOLUTIONLEVEL_City;
   };
   // если НПС из Монастыря
   if(C_NpcBelongsToMonastery(slf))
   {
       return ABSOLUTIONLEVEL_Monastery;
   };
   // если НПС с Фермы наемников
   if(C_NpcBelongsToFarm(slf))
   {
       return ABSOLUTIONLEVEL_Farm;
   };
   // если НПС из Лагеря бандитов
   if(C_NpcBelongsToBL(slf))
   {
       return ABSOLUTIONLEVEL_BL;
   };
   return 0;
};

// *************************************************
// Получить суммарный уровень криминала по локациям
// *************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf - НПС, проживающий в локации
// --------------------------------------
// Возвращаемое значение - суммарный уровень криминала ГГ для локации
// ======================================

func int B_GetTotalPetzCounter(var c_npc slf)
{
   // если НПС из Миненталя
   if (C_NpcBelongsToOldCamp(slf))
   {
       // суммарный уровень криминала для локации
       return (PETZCOUNTER_OldCamp_Murder + PETZCOUNTER_OldCamp_Theft + PETZCOUNTER_OldCamp_Attack + PETZCOUNTER_OldCamp_Sheepkiller);
   };
   // если НПС из Хориниса
   if (C_NpcBelongsToCity(slf))
   {
       return (PETZCOUNTER_City_Murder + PETZCOUNTER_City_Theft + PETZCOUNTER_City_Attack + PETZCOUNTER_City_Sheepkiller);
   };
   // если НПС из Монастыря
   if (C_NpcBelongsToMonastery(slf))
   {
       return (PETZCOUNTER_Monastery_Murder + PETZCOUNTER_Monastery_Theft + PETZCOUNTER_Monastery_Attack + PETZCOUNTER_Monastery_Sheepkiller);
   };
   // если НПС с Фермы наемников
   if (C_NpcBelongsToFarm(slf))
   {
       return (PETZCOUNTER_Farm_Murder + PETZCOUNTER_Farm_Theft + PETZCOUNTER_Farm_Attack + PETZCOUNTER_Farm_Sheepkiller);
   };
   // если НПС из Лагеря бандитов
   if (C_NpcBelongsToBL(slf))
   {
       return (PETZCOUNTER_BL_Murder + PETZCOUNTER_BL_Theft + PETZCOUNTER_BL_Attack);
   };
   return 0;
};

// *********************************************************
// Получить криминал ГГ по локациям и в порядке приоритетов
// *********************************************************

// ======================================
// Аргументы:
// --------------------------------------
// slf - НПС, проживающий в локации
// --------------------------------------
// Возвращаемое значение - приоритетный криминал для локации
// ======================================

func int B_GetGreatestPetzCrime(var c_npc slf)
{
   // если НПС из Миненталя
   if(C_NpcBelongsToOldCamp(slf))
   {
       // если счетчик убийств > 0
       if(PETZCOUNTER_OldCamp_Murder > 0)
       {
           // криминал - убийство
           return CRIME_MURDER;
       };
       // если счетчик воровства > 0
       if(PETZCOUNTER_OldCamp_Theft > 0)
       {
           // криминал - воровство
           return CRIME_THEFT;
       };
       // если счетчик нападений > 0
       if(PETZCOUNTER_OldCamp_Attack > 0)
       {
           // криминал - нападение
           return CRIME_ATTACK;
       };
       // если счетчик убийств овец > 0
       if(PETZCOUNTER_OldCamp_Sheepkiller > 0)
       {
           // криминал - убийство овец
           return CRIME_SHEEPKILLER;
       };
   };
   // если НПС из Хориниса
   if(C_NpcBelongsToCity(slf))
   {
       if(PETZCOUNTER_City_Murder > 0)
       {
           return CRIME_MURDER;
       };
       if(PETZCOUNTER_City_Theft > 0)
       {
           return CRIME_THEFT;
       };
       if(PETZCOUNTER_City_Attack > 0)
       {
           return CRIME_ATTACK;
       };
       if(PETZCOUNTER_City_Sheepkiller > 0)
       {
           return CRIME_SHEEPKILLER;
       };
   };
   // если НПС из Монастыря
   if(C_NpcBelongsToMonastery(slf))
   {
       if(PETZCOUNTER_Monastery_Murder > 0)
       {
           return CRIME_MURDER;
       };
       if(PETZCOUNTER_Monastery_Theft > 0)
       {
           return CRIME_THEFT;
       };
       if(PETZCOUNTER_Monastery_Attack > 0)
       {
           return CRIME_ATTACK;
       };
       if(PETZCOUNTER_Monastery_Sheepkiller > 0)
       {
           return CRIME_SHEEPKILLER;
       };
   };
   // если НПС с Фермы наемников
   if(C_NpcBelongsToFarm(slf))
   {
       if(PETZCOUNTER_Farm_Murder > 0)
       {
           return CRIME_MURDER;
       };
       if(PETZCOUNTER_Farm_Theft > 0)
       {
           return CRIME_THEFT;
       };
       if(PETZCOUNTER_Farm_Attack > 0)
       {
           return CRIME_ATTACK;
       };
       if(PETZCOUNTER_Farm_Sheepkiller > 0)
       {
           return CRIME_SHEEPKILLER;
       };
   };
   // если НПС из Лагеря бандитов
   if(C_NpcBelongsToBL(slf))
   {
       if(PETZCOUNTER_BL_Murder > 0)
       {
           return CRIME_MURDER;
       };
       if(PETZCOUNTER_BL_Theft > 0)
       {
           return CRIME_THEFT;
       };
       if(PETZCOUNTER_BL_Attack > 0)
       {
           return CRIME_ATTACK;
       };
   };
   return CRIME_NONE;
};


13. Вспомогательные С_ функции для людей (compare функции).

Все С_ функции расположены в директории ..\AI\Human\С_Human\
Следующие два файла можно удалить по причине их бесполезности: C_BanditAttackBandit.d и C_BanditHelpsStoryBandit.d
// ********************************************************************
// Проверка состояния тела НПС
// --------------------------------------------------------------------
// Примечание автора: Никогда не используйте встроенную функцию Npc_GetBodyState
// напрямую, т.к. сравнение всегда будет ложно, если установлены дополнительные
// флаги состояния тела. Все запросы на состояние тела выполняйте только
// через эту функцию.
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС, состояние тела которого проверяется
// bodystate - проверяемое состояние тела
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - состояние тела НПС совпадает с проверяемым, FALSE - не совпадает
// ====================================================================

func int C_BodyStateContains(var c_npc slf,var int bodystate)
{
   // Примечание автора: Руки прочь от этой формулы!!! Я знаю точно что я делаю! (Мое примечание: с точки зрения логического анализа формула неправильна).
   // если фактическое состояние тела НПС без учета допфлагов равно запрошенному состоянию без учета допфлагов
   if((Npc_GetBodyState(slf)&(BS_MAX|BS_FLAG_INTERRUPTABLE|BS_FLAG_FREEHANDS)) == (bodystate&(BS_MAX|BS_FLAG_INTERRUPTABLE|BS_FLAG_FREEHANDS)))
   {
       return TRUE;
   };
   return FALSE;
};

// ********************************************************************************
// Убивается ли НПС по окончании жизни или переводится в бессознательное состояние
// ********************************************************************************

// ====================================================================
// self - проверяемый НПС
// other - преступник, убивающий НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - НПС не убивается преступником, FALSE - убивается.
// ====================================================================

func int C_DropUnconscious()
{
   // если преступник монстр или Черный маг (Ищущий) или бандит или преступнику разрешено убить НПС
   if(other.guild > GIL_SEPERATOR_HUM) || (other.guild == GIL_DMT) || (other.guild == GIL_BDT) || (other.aivar[AIV_DropDeadAndKill] == TRUE)
   {
       // НПС будет убит
       return FALSE;
   };
   // если НПС человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // если НПС не Черный маг и не бандит и его не разрешено убивать преступнику
       if(self.guild != GIL_DMT) && (self.guild != GIL_BDT) && (self.aivar[AIV_DropDeadAndKill] == FALSE)
       {
           // НПС будет безсознания
           return TRUE;
       };
   };
   // НПС будет убит
   return FALSE;
};

// *****************************************************************
// Может ли быть взят предмет, принадлежащий одному НПС, другим НПС
// *****************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС, которому может принадлежать предмет
// oth - НПС берущий предмет (ГГ)
// itm - предмет
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - предмет взять нельзя, FALSE - взять можно.
// ====================================================================

func int C_IsTakenItemMyPossession(var c_npc slf,var c_npc oth,var c_item itm)
{
   var int portalguild;
   // получить номер гильдии, которой принадлежит помещение (-1 помещение никому не принадлежит)
   portalguild = Wld_GetPlayerPortalGuild();
   // Моё примечание: предыдущие две строки лишние и могут быть удалены.
   // если предмет принадлежит НПС владельцу
   if(Npc_OwnedByNpc(itm,slf))
   {
       // если предмет Святой молот
       if(Hlp_IsItem(itm,Holy_Hammer_MIS))
       {
           // Святой молот взят
           Hammer_Taken = TRUE;
       };
       // брать нельзя
       return TRUE;
   };
   // если предмет выброшен (просто валяется)
   if((itm.flags & ITEM_DROPPED) == ITEM_DROPPED)
   {
       // брать можно
       return FALSE;
   };
   // если ГГ находится в помещении, принадлежащем НПС или помещение публично
   // Моё примечание: правильнее вместо self написать slf, а то в некоторых случаях может быть ошибка.
   if(C_NpcIsBotheredByPlayerRoomGuild(self)) || (Wld_GetPlayerPortalGuild() == GIL_PUBLIC)
   {
       // брать нельзя
       return TRUE;
   };
   // если отношения между гильдиями НПС и гильдией, владеющей предметом, дружественные
   if(Wld_GetGuildAttitude(slf.guild,itm.ownerguild) == ATT_FRIENDLY)
   {
       // брать нельзя
       return TRUE;
   };
   // брать можно
   return FALSE;
};

// ****************************************************************
// Можно ли импользовать MOB, принадлежащий одному НПС, другим НПС
// ****************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС, которому может принадлежать MOB
// oth - НПС использующий MOB (ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - MOB использовать нельзя, FALSE - можно.
// ====================================================================

func int C_IsUsedMobMyPossession(var c_npc slf,var c_npc oth)
{
   // Моё примечание: правильнее вместо self и other внутри функции написать slf и oth, а то в некоторых случаях может быть ошибка.
   // если MOB, используемый ГГ, принадлежит НПС
   if(Npc_IsDetectedMobOwnedByNpc(other,self))
   {
       // нельзя
       return TRUE;
   };
   // если ГГ находится в помещении, принадлежащем НПС или помещение публично
   if(C_NpcIsBotheredByPlayerRoomGuild(self)) || (Wld_GetPlayerPortalGuild() == GIL_PUBLIC)
   {
       // нельзя
       return TRUE;
   };
   // можно
   return FALSE;
};

// ********************************************************************
// Функции проверяющие принадлежность НПС к определенной локации
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - принадлежит, FALSE - нет.
// ====================================================================

// Проверка принадлежности к Миненталю
// --------------------------------------------------------------------
func int C_NpcBelongsToOldCamp(var c_npc slf)
{
   // если НПС значим или из окружающего народа в Минентале
   if(slf.npctype == NPCTYPE_OCMAIN) || (slf.npctype == NPCTYPE_OCAMBIENT)
   {
       // если НПС Горожанин или Милиционер или Паладин
       if(slf.guild == GIL_VLK) || (slf.guild == GIL_MIL) || (slf.guild == GIL_PAL)
       {
           return TRUE;
       };
   };
   return FALSE;
};

// Проверка принадлежности к Хоринису
// --------------------------------------------------------------------
func int C_NpcBelongsToCity(var c_npc slf)
{
   // если НПС не из Миненталя
   if(!C_NpcBelongsToOldCamp(slf))
   {
       // если НПС Горожанин или Милиционер или Паладин
       if(slf.guild == GIL_VLK) || (slf.guild == GIL_MIL) || (slf.guild == GIL_PAL)
       {
           return TRUE;
       };
   };
   return FALSE;
};

// Проверка принадлежности к Монастырю
// ---------------------------------------------------------------------
func int C_NpcBelongsToMonastery(var c_npc slf)
{
   // если НПС Маг огня или Маг послушник
   if(slf.guild == GIL_KDF) || (slf.guild == GIL_NOV)
   {
       return TRUE;
   };
   return FALSE;
};

// Проверка принадлежности к Ферме наемников
// ---------------------------------------------------------------------
func int C_NpcBelongsToFarm(var c_npc slf)
{
   // если НПС Крестьянин или Наемник
   if(slf.guild == GIL_BAU) || (slf.guild == GIL_SLD)
   {
       return TRUE;
   };
   return FALSE;
};

// Проверка принадлежности к Лагерю бандитов
// ---------------------------------------------------------------------
func int C_NpcBelongsToBL(var c_npc slf)
{
   // если НПС из окружающего народа в Лагере или Долине бандитов
   if(slf.npctype == NPCTYPE_BL_AMBIENT) || (slf.npctype == NPCTYPE_BL_MAIN)
   {
       return TRUE;
   };
   return FALSE;
};

// ******************************************
// Имеет ли НПС новости об окружающей жизни
// ******************************************

// ==========================================
// Аргументы:
// ------------------------------------------
// slf - проверяемый НПС
// ------------------------------------------
// Возвращаемое значение:
// TRUE - новости есть, FALSE - нет.
// ==========================================

func int C_NpcHasAmbientNews(var c_npc slf)
{
   // если НПС не знает никаких новостей
   if(slf.aivar[AIV_NewsOverride] == TRUE)
   {
       return FALSE;
   };
   // если ГГ не имеет криминала
   if(B_GetPlayerCrime(slf) == CRIME_NONE)
   {
       return FALSE;
   };
   // новости есть (по умолчанию)
   return TRUE;
};

// *****************************************
// Является ли причиной атаки убийство цели
// *****************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС агрессор
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - причина атаки - убийство, FALSE - нет.
// ====================================================================

func int C_NpcHasAttackReasonToKill(var c_npc slf)
{
   // если
   if (slf.aivar[AIV_ATTACKREASON] == AR_KILL)                 // ГГ убил НПС
   || (slf.aivar[AIV_ATTACKREASON] == AR_GuildEnemy)           // или НПС является врагом гильдии
   || (slf.aivar[AIV_ATTACKREASON] == AR_HumanMurderedHuman)   // или Человек убил человека
   || (slf.aivar[AIV_ATTACKREASON] == AR_GuardStopsIntruder)   // или Охрана ворот атакует незваного гостя
   || (slf.aivar[AIV_ATTACKREASON] == AR_GuardCalledToKill)    // или Вызвана охрана по факту убийства
   {
       return TRUE;
   };
   return FALSE;
};

// ************************************
// Находится ли ГГ в частном помещении
// ************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС, которому может принадлежать помещение
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - ГГ в частном помещении, FALSE - нет.
// ====================================================================

func int C_NpcIsBotheredByPlayerRoomGuild(var c_npc slf)
{
   var int portalguild;
   // получить номер гильдии, которой принадлежит помещение (-1 помещение никому не принадлежит)
   portalguild = Wld_GetPlayerPortalGuild();
   // если помещение принадлежит гильдии и (НПС из этой гильдии или гильдия НПС и гильдия, владеющая помещением, дружественны)
   if(portalguild > GIL_NONE) && ((slf.guild == portalguild) || (Wld_GetGuildAttitude(slf.guild,portalguild) == ATT_FRIENDLY))
   {
       return TRUE;
   };
   return FALSE;
};

// *****************************************************************
// Реагирует ли цель на оружие, направленное против нее агрессором
// *****************************************************************

// =================================================================
// Аргументы:
// -----------------------------------------------------------------
// slf - НПС цель
// oth - НПС агрессор
// -----------------------------------------------------------------
// Возвращаемое значение:
// TRUE - цель реагирует, FALSE - нет.
// =================================================================

func int C_NpcIsBotheredByWeapon(var c_npc slf,var c_npc oth)
{
   // если цель член партии ГГ
   if(slf.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       return FALSE;
   };
   // если цель друг ГГ и агрессор ГГ (Примечание: правильней вместо other написать oth)
   if(slf.npctype == NPCTYPE_FRIEND) && (Npc_IsPlayer(other))
   {
       return FALSE;
   };
   // если НПС имеет дело с "крутым парнем" и агрессор сражается оружием ближнего радиуса поражения
   if(C_NpcIsToughGuy(slf)) && (Npc_IsInFightMode(oth,FMODE_MELEE))
   {
       return FALSE;
   };
   // если отношения между агрессором и целью дружественны
   if(Npc_GetAttitude(slf,oth) == ATT_FRIENDLY)
   {
       return FALSE;
   };
   // если цель является охранником ворот
   if(C_NpcIsGateGuard(slf))
   {
       return FALSE;
   };
   // если агрессор сражается кулаками
   if(Npc_IsInFightMode(oth,FMODE_FIST))
   {
       return FALSE;
   };
   // если агрессор использует магию и категория используемого заклинания безвредна
   if(Npc_IsInFightMode(oth,FMODE_MAGIC) && (Npc_GetActiveSpellCat(oth) != SPELL_BAD))
   {
       return FALSE;
   };
   // если цель Черный маг или орк
   if((slf.guild == GIL_DMT) || (slf.guild == GIL_ORC))
   {
       return FALSE;
   };
   return TRUE;
};

// **********************************************
// Находится ли НПС в бессознательном состоянии
// **********************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - НПС в бессознательном состоянии (обездвижен), FALSE - нет.
// ====================================================================

func int C_NpcIsDown(var c_npc slf)
{
   // если НПС безсознания или в магическом сне или убит
   if(Npc_IsInState(slf,ZS_Unconscious) || Npc_IsInState(slf,ZS_MagicSleep) || Npc_IsDead(slf))
   {
       return TRUE;
   };
   return FALSE;
};

// ***********************************
// Является ли НПС источником зла
// ***********************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - НПС - источник зла, FALSE - нет.
// ====================================================================

func int C_NpcIsEvil(var c_npc slf)
{
   // если НПС
   if (slf.guild == GIL_DMT)                           // Черный маг
   || (slf.guild == GIL_DRAGON)                        // или дракон
   || (slf.guild == GIL_ORC)                           // или орк
   || (slf.guild == GIL_DRACONIAN)                     // или ящер
   || (slf.guild == GIL_DEMON)                         // или демон
   || (slf.guild == GIL_SUMMONED_DEMON)                // или вызванный демон
   || (slf.aivar[AIV_MM_REAL_ID] == ID_TROLL_BLACK)    // или Черный троль
   || (C_NpcIsUndead(slf))                             // или НПС является нежитью
   {
       return TRUE;
   };
   return FALSE;
};

// *************************************
// Является ли НПС охранником ворот
// *************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_NpcIsGateGuard(var c_npc slf)
{
   // если НПС находится в состоянии охраны ворот
   if(Npc_IsInState(slf,ZS_Guard_Passage))
   {
       return TRUE;
   };
   return FALSE;
};

// *************************************
// Является ли НПС ГГ
// *************************************

// =====================================
// Аргументы:
// -------------------------------------
// slf - проверяемый НПС
// -------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// =====================================

func int C_NpcIsHero(var c_npc slf)
{
   var c_npc her;
   // получить ссылку на ГГ
   her = Hlp_GetNpc(PC_Hero);
   // если идентификаторы НПС и ГГ равны
   if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(her))
   {
       return TRUE;
   };
   return FALSE;
};

// ***********************************************
// Является ли НПС "крутым парнем"
// ***********************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_NpcIsToughGuy(var c_npc slf)
{
   // если установлен признак "крутого парня"
   if(slf.aivar[AIV_ToughGuy] == TRUE)
   {
       return TRUE;
   };
   // если НПС наемник или охотник на драконов или бандит или пират
   if(slf.guild == GIL_SLD) || (slf.guild == GIL_DJG) || (slf.guild == GIL_BDT) || (slf.guild == GIL_PIR)
   {
       return TRUE;
   };
   return FALSE;
};

// ***************************************
// Является ли НПС нежитью
// ***************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_NpcIsUndead(var c_npc slf)
{
   // если НПС
    if (slf.guild == GIL_GOBBO_SKELETON)               // Скелет гоблина
    || (slf.guild == GIL_SUMMONED_GOBBO_SKELETON)      // или Вызванный скелет гоблина
    || (slf.guild == GIL_SKELETON)                     // или Скелет
    || (slf.guild == GIL_SUMMONED_SKELETON)            // или Вызванный скелет
    || (slf.guild == GIL_SKELETON_MAGE)                // или Скелет маг
    || (slf.guild == GIL_ZOMBIE)                       // или Зомби
    || (slf.guild == GIL_SHADOWBEAST_SKELETON)         // или Скелет мракориса
    || (slf.guild == GIL_UNDEADORC)                    // или Орк нежить
    || (slf.aivar[AIV_MM_REAL_ID] == ID_DRAGON_UNDEAD) // или Дракон нежить
    {
       return TRUE;
    };
    return FALSE;
};

// ************************************************
// Должен ли НПС обыскать тело
// ************************************************

// ================================================
// Аргументы:
// ------------------------------------------------
// slf - обыскивающий НПС
// неявно: other - обыскиваемый НПС (цель)
// ------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ================================================

func int C_NpcRansacksAlways(var c_npc slf)
{
   // если НПС Гарвиг и цель имеет "Святой молот"
   if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Garwig)) && (Npc_HasItems(other,Holy_Hammer_MIS) > 0)
   {
       return TRUE;
   };
   // если НПС Род и цель имеет "Меч Рода"
   if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Rod)) && (Npc_HasItems(other,ItMw_2h_Rod) > 0)
   {
       return TRUE;
   };
   return FALSE;
};

// ********************************************************************
// Является ли НПС членом подставной гильдии
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС определяющий подставу
// oth - проверяемый НПС (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да (определяется что ГГ переодет), FALSE - нет.
// ====================================================================

func int C_PlayerHasFakeGuild(var c_npc slf,var c_npc oth)
{
   var c_item itm;
   // получить доспехи ГГ
   itm = Npc_GetEquippedArmor(oth);
   // Примечание: следующие две строки не нужны при условии коррекции функции ниже
   var int NSC_CommentRangerArmor;
   NSC_CommentRangerArmor = TRUE;
   // если НПС игнорирует доспехи, в которые одет ГГ
   if(slf.aivar[AIV_IgnoresArmor] == TRUE)
   {
       return FALSE;
   };
   // если НПС игнорирует подставную гильдию
   if(slf.aivar[AIV_IgnoresFakeGuild] == TRUE)
   {
       return FALSE;
   };
   // если НПС является охранником ворот (Примечание: правильнее вместо self написать slf)
   if(C_NpcIsGateGuard(self) == TRUE)
   {
       return FALSE;
   };
   // если НПС бандит и ГГ переодетый в бандита
   if(slf.guild == GIL_BDT) && C_PlayerIsFakeBandit(slf,oth)
   {
       // если ГГ без брони
       if(Npc_HasEquippedArmor(oth) == FALSE)
       {
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если ГГ без брони
   if(Npc_HasEquippedArmor(oth) == FALSE)
   {
       return FALSE;
   }
   // если гильдии НПС и ГГ одинаковы (Примечание: else здесь лишнее)
   else if(slf.guild == oth.guild)
   {
       // если доспехи ГГ "Кольчуга 'Кольца Воды'"
       if(Hlp_IsItem(itm,ITAR_RANGER_Addon) == TRUE)
       {
           // Примечание: выполняется всегда, поэтому условие if можно удалить, оставив только return TRUE;
           if(NSC_CommentRangerArmor == TRUE)
           {
               return TRUE;
           }
           // Примечание: блок else можно удалить
           else
           {
               return FALSE;
           };
       };
       // если ГГ не принадлежит к гильдии и одет в
       if(oth.guild == GIL_NONE)
       && ((Hlp_IsItem(itm,ITAR_VLK_L) == TRUE)        // "Одежду горожанина"
       || (Hlp_IsItem(itm,ITAR_VLK_M) == TRUE)         // или "Одежду горожанина"
       || (Hlp_IsItem(itm,ITAR_VLK_H) == TRUE)         // или "Одежду горожанина" (Примечание: кто может пояснить, чем отличаются эти одежды?)
       || (Hlp_IsItem(itm,ITAR_BAU_L) == TRUE)         // или "Одежду крестьянина"
       || (Hlp_IsItem(itm,ITAR_BAU_M) == TRUE)         // или "Одежду крестьянина 2"
       || (Hlp_IsItem(itm,ITAR_Leather_L) == TRUE))    // или "Кожаные доспехи"
       {
           return FALSE;
       }
       // Примечание: далее до конца блока все else можно удалить
       // если ГГ милиционер и одет в
       else if(oth.guild == GIL_MIL)
       && ((Hlp_IsItem(itm,ITAR_MIL_L) == TRUE)        // "Легкие доспехи ополчения"
       || (Hlp_IsItem(itm,ITAR_MIL_M) == TRUE))        // или "Тяжелые доспехи ополчения"
       {
           return FALSE;
       }
       // если ГГ паладин и одет в
       else if(oth.guild == GIL_PAL)
       && ((Hlp_IsItem(itm,ITAR_PAL_M) == TRUE)        // "Доспехи паладина"
       || (Hlp_IsItem(itm,ITAR_PAL_H) == TRUE))        // или "Доспехи паладина" (улучшенные)
       {
           return FALSE;
       }
       // если ГГ наемник и одет в
       else if(oth.guild == GIL_SLD)
       && ((Hlp_IsItem(itm,ITAR_SLD_L) == TRUE)        // "Легкие доспехи наемника"
       || (Hlp_IsItem(itm,ITAR_SLD_M) == TRUE)         // или "Средние доспехи наемника"
       || (Hlp_IsItem(itm,ITAR_SLD_H) == TRUE))        // или "Тяжелые доспехи наемника"
       {
           return FALSE;
       }
       // если ГГ охотник на драконов и одет в
       else if(oth.guild == GIL_DJG)
       && ((Hlp_IsItem(itm,ITAR_SLD_L) == TRUE)        // "Легкие доспехи наемника"
       || (Hlp_IsItem(itm,ITAR_SLD_M) == TRUE)         // или "Средние доспехи наемника"
       || (Hlp_IsItem(itm,ITAR_SLD_H) == TRUE)         // или "Тяжелые доспехи наемника"
       || (Hlp_IsItem(itm,ITAR_DJG_L) == TRUE)         // или "Легкие доспехи охотника на драконов"
       || (Hlp_IsItem(itm,ITAR_DJG_M) == TRUE)         // или "Средние доспехи охотника на драконов"
       || (Hlp_IsItem(itm,ITAR_DJG_H) == TRUE)         // или "Тяжелые доспехи охотника на драконов"
       || (Hlp_IsItem(itm,ITAR_DJG_Crawler) == TRUE))  // или "Доспехи из панцирей ползунов"
       {
           return FALSE;
       }
       // если ГГ маг послушник и одет в "Робу послушника"
       else if(oth.guild == GIL_NOV) && (Hlp_IsItem(itm,ITAR_NOV_L) == TRUE)
       {
           return FALSE;
       }
       // если ГГ маг огня и одет в
       else if(oth.guild == GIL_KDF)
       && ((Hlp_IsItem(itm,ITAR_KDF_L) == TRUE)        // "Робу мага огня"
       || (Hlp_IsItem(itm,ITAR_KDF_H) == TRUE))        // или "Тяжелую робу огня"
       {
           return FALSE;
       }
       else
       {
           return TRUE;
       };
   };
   return FALSE;
};

// ********************************************************************
// Является ли НПС членом гильдии бандитов
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС определяющий подставу
// oth - проверяемый НПС (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да (ГГ "принадлежит" к бандитам), FALSE - нет.
// ====================================================================

func int C_PlayerIsFakeBandit(var c_npc slf,var c_npc oth)
{
   var c_item itm;
   // если НПС игнорирует доспехи, в которые одет ГГ
   if(slf.aivar[AIV_IgnoresArmor] == TRUE)
   {
       return FALSE;
   };
   // если НПС игнорирует подставную гильдию
   if(slf.aivar[AIV_IgnoresFakeGuild] == TRUE)
   {
       return FALSE;
   };
   // если НПС пират
   if(slf.guild == GIL_PIR)
   {
       return FALSE;
   };
   // если ГГ одет в броню
   if(Npc_HasEquippedArmor(oth) == TRUE)
   {
       // получить доспехи ГГ
       itm = Npc_GetEquippedArmor(oth);
       // если доспехи ГГ
       if((Hlp_IsItem(itm,ITAR_BDT_M) == TRUE)         // "Средняя бандитская кольчуга"
       || (Hlp_IsItem(itm,ItAR_Thorus_Addon) == TRUE)  // или "Тяжелая кольчуга гардиста"
       || (Hlp_IsItem(itm,ITAR_BDT_H) == TRUE))        // или "Тяжелая бандитская кольчуга"
       {
           return TRUE;
       };
       return FALSE;
   }
   // если НПС бандит и текущий уровень - Яркендар и ГГ может разговаривать с бандитами (Равен убит)
   if(slf.guild == GIL_BDT) && (CurrentLevel == ADDONWORLD_ZEN) && (Player_HasTalkedToBanditCamp == TRUE)
   {
       return TRUE;
   };
   return FALSE;
};

// ********************************************************************
// Является ли НПС членом гильдии пиратов
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС определяющий подставу
// oth - проверяемый НПС (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да (ГГ "принадлежит" к пиратам), FALSE - нет.
// ====================================================================

func int C_PlayerIsFakePirate(var c_npc slf,var c_npc oth)
{
   var c_item itm;
   // если НПС игнорирует доспехи, в которые одет ГГ
   if(slf.aivar[AIV_IgnoresArmor] == TRUE)
   {
       return FALSE;
   };
   // если НПС игнорирует подставную гильдию
   if(slf.aivar[AIV_IgnoresFakeGuild] == TRUE)
   {
       return FALSE;
   };
   // если ГГ одет в броню (Примечание: правильнее вместо other написать oth)
   if(Npc_HasEquippedArmor(other) == TRUE)
   {
       // получить доспехи ГГ
       itm = Npc_GetEquippedArmor(oth);
       // если доспехи ГГ
       if((Hlp_IsItem(itm,ITAR_PIR_L_Addon) == TRUE)   // "Одежда пирата"
       || (Hlp_IsItem(itm,ItAR_PIR_M_Addon) == TRUE)   // или "Доспехи пирата"
       || (Hlp_IsItem(itm,ITAR_PIR_H_Addon) == TRUE))  // или "Одежда капитана"
       {
           return TRUE;
       };
   };
   return FALSE;
};

// *************************************************
// Отказывается ли НПС от предлагаемого разговора
// *************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС, которому предложили разговор
// oth - инициатор разговора (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_RefuseTalk(var c_npc slf,var c_npc oth)
{
   // если НПС не может говорить и он охраняет ворота и он не выдавал предупреждений
   if(Npc_RefuseTalk(slf) == TRUE) && (C_NpcIsGateGuard(slf)) && (slf.aivar[AIV_Guardpassage_Status] == GP_NONE)
   {
       return TRUE;
   };

   // если НПС с ГГ не разговаривал и НПС находится от своего вайпоинта далее 5м
   if(slf.aivar[AIV_TalkedToPlayer] == FALSE) && (Npc_GetDistToWP(slf,slf.wp) > 500)
   {
       // если НПС Лотар
       if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Lothar))
       {
           return TRUE;
       };
   };
   // если НПС определил, что ГГ член подставной гильдии и НПС неуязвимый (Примечание: необходимо self заменить на slf)
   if(C_PlayerHasFakeGuild(slf,oth) && (self.flags != NPC_FLAG_IMMORTAL)
   {
       return TRUE;
   };
   return FALSE;
};

// *************************************
// Должен ли НПС атаковать убийцу
// *************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// oth - НПС убийца (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToAttackMurder(var c_npc slf,var c_npc oth)
{
   // если НПС Черный маг или Заключенный или Охотник на драконов или Бандит
   if(slf.guild == GIL_DMT) || (slf.guild == GIL_STRF) || (slf.guild == GIL_DJG) || (slf.guild == GIL_BDT)
   {
       return FALSE;
   };
   // если НПС игнорирует убийство
   if(slf.aivar[AIV_IGNORE_Murder] == TRUE)
   {
       return FALSE;
   };
   // если убийца ГГ и НПС ему друг
   if(Npc_IsPlayer(oth) && (slf.npctype == NPCTYPE_FRIEND))
   {
       return FALSE;
   };
   // если НПС убийца должен быть убить цель
   if(C_NpcHasAttackReasonToKill(oth))
   {
       return FALSE;
   };
   // если НПС охраняет ворота
   if(C_NpcIsGateGuard(slf))
   {
       return FALSE;
   };
   return TRUE;
};

// ********************************************************
// Должен ли НПС напасть на незваного гостя
// ********************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToAttackRoomIntruder(var c_npc slf)
{
   // Примечание: необходимо во всей функции self заменить на slf.
   // если НПС Милиционер или Паладин или Наемник или Охотник на драконов или ГГ имеет совершенный криминал
   if(self.guild == GIL_MIL) || (self.guild == GIL_PAL) || (self.guild == GIL_SLD) || (self.guild == GIL_DJG) || (B_GetPlayerCrime(self) != CRIME_NONE)
   {
       return TRUE;
   };
   return FALSE;
};

// **************************************
// Должен ли НПС атаковать убийцу овец
// **************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// oth - НПС убийца (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToAttackSheepKiller(var c_npc slf,var c_npc oth)
{
   // если НПС Черный маг или Заключенный или Бандит или Охотник на драконов
   if(slf.guild == GIL_DMT) || (slf.guild == GIL_STRF) || (slf.guild == GIL_BDT) || (slf.guild == GIL_DJG)
   {
       return FALSE;
   };
   // если НРС игнорирует убийство овец
   if(slf.aivar[AIV_IGNORE_Sheepkiller] == TRUE)
   {
       return FALSE;
   };
   // если убийца ГГ и НПС ему друг
   if(Npc_IsPlayer(oth) && (slf.npctype == NPCTYPE_FRIEND))
   {
       return FALSE;
   };
   // если убийца должен был убить овцу
   if(C_NpcHasAttackReasonToKill(oth))
   {
       return FALSE;
   };
   // если убийца человек и у него была причина нападения (Примечание: необходимо other заменить на oth)
   if(other.guild < GIL_SEPERATOR_HUM) && (other.aivar[AIV_ATTACKREASON] != AR_NONE)
   {
       return FALSE;
   };
   // если НПС охраняет ворота
   if(C_NpcIsGateGuard(slf))
   {
       return FALSE;
   };
   return TRUE;
};

// ******************************************
// Должен ли НПС атаковать вора
// ******************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// oth - НПС вор (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToAttackThief(var c_npc slf,var c_npc oth)
{
   // если НПС Черный ман или Заключенный или Охотник на драконов
   if(slf.guild == GIL_DMT) || (slf.guild == GIL_STRF) || (slf.guild == GIL_DJG)
   {
       return FALSE;
   };
   // если НПС не реагирует на воровство
   if(slf.aivar[AIV_IGNORE_Theft] == TRUE)
   {
       return FALSE;
   };
   // если НПС проиграл последнее сражение с ГГ
   if(slf.aivar[AIV_LastFightAgainstPlayer] == FIGHT_LOST)
   {
       return FALSE;
   };
   // если вор ГГ и НПС ему друг
   if(Npc_IsPlayer(oth) && (slf.npctype == NPCTYPE_FRIEND))
   {
       return FALSE;
   };
   // если НПС охраняет ворота
   if(C_NpcIsGateGuard(slf))
   {
       return FALSE;
   };
   return TRUE;
};

// *********************************
// Должен ли НПС убежать от цели
// *********************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// oth - цель (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToFlee(var c_npc slf,var c_npc oth)
{
   // никогда не убегает!!!
   return FALSE;
};

// *********************************
// Должен ли НПС обыскать тело
// *********************************

// =================================
// Аргументы:
// ---------------------------------
// slf - проверяемый НПС
// other - тело (задано неявно)
// ---------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// =================================

func int C_WantToRansack(var c_npc slf)
{
   // если НРС друг ГГ и тело есть ГГ
   if(slf.npctype == NPCTYPE_FRIEND) && (Npc_IsPlayer(other))
   {
       return FALSE;
   };
   // если НПС Равен
   if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Raven))
   {
       return FALSE;
   };
   // если НПС Черный маг или Орк или Паладин или Маг огня или Послушник или Маг воды
   if(slf.guild == GIL_DMT) || (slf.guild == GIL_ORC) || (slf.guild == GIL_PAL) || (slf.guild == GIL_KDF) || (slf.guild == GIL_NOV) || (slf.guild == GIL_KDW)
   {
       return FALSE;
   };
   return TRUE;
};

// **********************************************
// Должен ли НПС реагировать на крадущегося ГГ
// **********************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый НПС
// oth - крадущийся НПС (обычно ГГ)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToReactToSneaker(var c_npc slf,var c_npc oth)
{
   // если расстояние между НПС и ГГ > 7м
   if(Npc_GetDistToNpc(slf,oth) > 700)
   {
       return FALSE;
   };
   // если НПС Черный маг или Заключенный или Бандит и НПС не принадлежит к Лагерю бандитов
   if(slf.guild == GIL_DMT) || (slf.guild == GIL_STRF) || ((slf.guild == GIL_BDT) && !C_NpcBelongsToBL(slf))
   {
       return FALSE;
   };
   // если НПС друг ГГ и ГГ крадется
   if(slf.npctype == NPCTYPE_FRIEND) && (Npc_IsPlayer(oth))
   {
       return FALSE;
   };
   // если отношения между НПС и крадущимся НПС дружеские
   if(Npc_GetAttitude(slf,oth) == ATT_FRIENDLY)
   {
       return FALSE;
   };
   // если НПС охраняет ворота (Примечание: необходимо self заменить на slf)
   if(C_NpcIsGateGuard(self))
   {
       return FALSE;
   };
   return TRUE;
};


14. Отношения между гильдиями.

Отношения между гильдиями людей задаются в файле AI\Human\Guilds.d в виде таблицы. Ссылки в скриптах на эту таблицу вы не найдете, она используется только экзешником.
Примечание: Таблицу необходимо смотреть в широкоформатном редакторе.
// размер таблицы
const int TAB_ANZAHL = 16 * 16;

// таблица отношений между гильдиями людей
const int GIL_ATTITUDES[TAB_ANZAHL] =
{
// ***Гильдии***   Отсутствует   Паладин       Милиционер    Горожанин     Маг огня      Послушник     Охотник драк. Наемник       Крестьянин    Бандит        Заключенный  Черный маг    Чузеземец     Пират         Маг воды      Публичная
/* Отсутствует  */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/*******************************************************************************
********************************************************************************
********************************************************************************
/* Паладин      */ ATT_NEUTRAL,  ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Милиционер   */ ATT_NEUTRAL,  ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Горожанин    */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/*******************************************************************************
********************************************************************************
********************************************************************************
/* Маг огня     */ ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Послушник    */ ATT_NEUTRAL,  ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/*******************************************************************************
********************************************************************************
********************************************************************************
/* Охотник драк.*/ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Наемник      */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Крестьянин   */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/*******************************************************************************
********************************************************************************
********************************************************************************
/* Бандит       */ ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_FRIENDLY, ATT_NEUTRAL, ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Заключенный  */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/* Черный маг   */ ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_HOSTILE,  ATT_NEUTRAL,  ATT_HOSTILE, ATT_FRIENDLY, ATT_HOSTILE,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,
/*******************************************************************************
********************************************************************************
********************************************************************************
/* Чужеземец    */ ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_FRIENDLY, ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,
/* Пират        */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,
/* Маг воды     */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_FRIENDLY, ATT_HOSTILE,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,  ATT_FRIENDLY, ATT_NEUTRAL,
/* Публичная    */ ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL, ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL,  ATT_NEUTRAL
};

Отношения между гильдиями монстров и гильдиями монстров и людей задаются в файле AI\Monster\B_Monster\B_InitMonsterAttitudes.d
Данный файл я приводить не буду, у него очень большой размер (около 160 Кб).
Все отношения задаются через вызов функции B_InitMonsterAttitudes(), эту функцию мы и рассмотрим: она расположена в этой же директории в одноименном файле.
// ********************************************************************
// Устанавливает отношения между гильдиями монстров и всеми остальными
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// fromGuild - гильдия для которой устанавливается отношение
// attitude - отношение
// toGuild - гильдия с которой устанавливается отношение
// ====================================================================

func void B_SetMonsterAttitude(var int fromGuild,var int attitude,var int toGuild)
{
   // если отношение устанавливается с людьми
   if(toGuild == GIL_SEPERATOR_HUM)
   {
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_NONE);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_PAL);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_MIL);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_VLK);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_KDF);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_NOV);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_DJG);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_SLD);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_BAU);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_BDT);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_STRF);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_OUT);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_PIR);
       Wld_SetGuildAttitude(fromGuild,attitude,GIL_KDW);
   }
   // если отношение устанавливается для людей
   else if(fromGuild == GIL_SEPERATOR_HUM)
   {
       Wld_SetGuildAttitude(GIL_NONE,attitude,toGuild);
       Wld_SetGuildAttitude(GIL_PAL, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_MIL, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_VLK, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_KDF, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_NOV, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_DJG, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_SLD, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_BAU, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_BDT, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_STRF,attitude,toGuild);
       Wld_SetGuildAttitude(GIL_OUT, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_PIR, attitude,toGuild);
       Wld_SetGuildAttitude(GIL_KDW, attitude,toGuild);
   }
   else // устанавливается отношение между монстрами
   {
       Wld_SetGuildAttitude(fromGuild,attitude,toGuild);
   };
};


15. Магия.

Общие магические функции расположены в директории AI\Magic
// ****************************************************
// Регистрация повреждения от магии и начисление экспы
// ****************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - НПС кастующий заклинание
// oth - НПС цель
// damage - повреждение, нанесенное заклинанием
// ====================================================================

func void B_MagicHurtNpc(var c_npc slf,var c_npc oth,var int damage)
{
   // уменьшить жизнь цели на величину повреждения
   Npc_ChangeAttribute(oth,ATR_HITPOINTS,-damage);
   // если цель мертва
   if(Npc_IsDead(oth))
   {
       // (если кастующий ГГ или кастующий член партии ГГ) и экспа за цель не была начислена
       if(Npc_IsPlayer(slf) || (slf.aivar[AIV_PARTYMEMBER]==TRUE)) && (oth.aivar[AIV_VictoryXPGiven] == FALSE)
       {
           // начисление ГГ экспы (уровень цели * 10) (Примечание: Ошибка - вместо self.level должно быть oth.level)
           B_GivePlayerXP(self.level * XP_PER_VICTORY);
           // установить НПС флаг начисления экспы
           oth.aivar[AIV_VictoryXPGiven] = TRUE;
       };
   };
};

// **************************************
// Подготовка заклинания к использованию
// **************************************

// ================================================
// Аргументы:
// ------------------------------------------------
// slf - НПС кастующий заклинание
// spell - заклинание
// mana - требуемое кол-во маны
// ================================================

func void B_ReadySpell(var c_npc slf,var int spell,var int mana)
{
   // если макс. мана < требуемой
   if(slf.attribute[ATR_MANA_MAX] < mana)
   {

       // увеличить макс. ману на величину требуемой
       Npc_ChangeAttribute(slf,ATR_MANA_MAX,mana);
   };
   // если мана < требуемой
   if(slf.attribute[ATR_MANA] < mana)
   {
       // увеличить ману на величину требуемой
       Npc_ChangeAttribute(slf,ATR_MANA,mana);
   };
   // если это заклинание уже применяется
   if(Npc_IsDrawingSpell(slf) == spell)
   {
       return;
   }
   // если это заклинание уже активно
   if(Npc_GetActiveSpell(slf) == spell)
   {
       return;
   }
   // если активно другое заклинание
   else if(Npc_GetActiveSpell(slf) != -1)
   {
       // убрать старое заклинание
       AI_RemoveWeapon(slf);
   };
   // взять заклинание в руку
   AI_ReadySpell(slf,spell,mana);
};

// *************************************************************
// Выбор заклинания кастующим НПС
// *************************************************************

// =============================================================
// Аргументы:
// -------------------------------------------------------------
// slf - НПС кастующий заклинание (агрессор)
// oth - НПС цель
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - заклинание готово, FALSE - нет.
// =============================================================

func int B_SelectSpell(var c_npc slf,var c_npc oth)
{
   // если агрессор друг ГГ и цель ГГ и цель человек
   if(slf.npctype == NPCTYPE_FRIEND) && Npc_IsPlayer(oth) && (oth.guild < GIL_SEPERATOR_HUM)
   {
       // если агрессор Маг огня или агрессор всегда использует магию
       if(slf.guild == GIL_KDF) || (slf.aivar[AIV_MagicUser] == MAGIC_ALWAYS)
       {
           // если агрессор не имеет руны Сон
           if(Npc_HasItems(slf,ItRu_Sleep) == 0)
           {
               // создать в инвентаре агрессора руну Сон
               CreateInvItems(slf,ItRu_Sleep,1);
           };
           // подготовка заклинания Сон
           B_ReadySpell(slf,SPL_Sleep,SPL_Cost_Sleep);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Черный маг и агрессор всегда использует магию
   if(slf.guild == GIL_DMT) && (slf.aivar[AIV_MagicUser] == MAGIC_ALWAYS)
   {
           // создать в инвентаре агрессора следующие руны, если он их не имеет
           if(Npc_HasItems(slf,ItRu_InstantFireball) == 0) {CreateInvItems(slf,ItRu_InstantFireball,1);};  // "Огненный шар"
           if(Npc_HasItems(slf,ItRu_Deathbolt) == 0)       {CreateInvItems(slf,ItRu_Deathbolt,1);};        // "Deathbolt" (Примечание: название не переведено)
           if(Npc_HasItems(slf,ItRu_Deathball) == 0)       {CreateInvItems(slf,ItRu_Deathball,1);};        // "Deathball"
           if(Npc_HasItems(slf,ItRu_Firerain) == 0)        {CreateInvItems(slf,ItRu_Firerain,1);};         // "Огненный дождь"
           if(Npc_HasItems(slf,ItRu_Thunderstorm) == 0)    {CreateInvItems(slf,ItRu_Thunderstorm,1);};     // "Шторм"
           if(Npc_HasItems(slf,ItRu_LightningFlash) == 0)  {CreateInvItems(slf,ItRu_LightningFlash,1);};   // "Удар молнии"
           if(Npc_HasItems(slf,ItRu_Firestorm) == 0)       {CreateInvItems(slf,ItRu_Firestorm,1);};        // "Малая огненная буря"
           if(Npc_HasItems(slf,ItRu_Skull) == 0)           {CreateInvItems(slf,ItRu_Skull,1);};            // "Крик мертвых"
       // если агрессор "Черный маг" (Примечание: Маг какой-то особенный, хотя в переводе не отражено)
       if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(DMT_1299_OberDementor_DI))
       {
           // подготовка заклинания "Крик мертвых"
           B_ReadySpell(slf,SPL_Skull,SPL_Cost_Skull);
           return TRUE;
       }
       else // обычные Черные маги
       {
           // если уже есть оружие готовое к бою
           if(Npc_IsDrawingWeapon(slf))
           {
               return TRUE;
           };
           // если случайное число не задано
           if(slf.aivar[AIV_SelectSpell] <= 0)
           {
               var int dK_rnd;
               dK_rnd = Hlp_Random(10);
               // получить случайное число
               slf.aivar[AIV_SelectSpell] += dK_rnd;
           };
           // если случайное число < 10
           if(slf.aivar[AIV_SelectSpell] < 10)
           {
               // если Глава <= 3, то подготовить заклинание "Огненный шар"
               if(Kapitel <= 3)        {B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball); return TRUE;}
               // если Глава <= 4, то подготовить заклинание "Deathbolt"
               else if (Kapitel <= 4)  {B_ReadySpell(slf,SPL_DeathBolt,SPL_Cost_DeathBolt); return TRUE;}
               // иначе подготовить заклинание "Deathbаll"
               else /*Kap 5+*/         {B_ReadySpell(slf,SPL_DeathBall,SPL_Cost_DeathBall); return TRUE;};
           }
           // если случайное число == 10
           else if(slf.aivar[AIV_SelectSpell] == 10)
           {
               // случайное число = 11
               slf.aivar[AIV_SelectSpell] = 11;
               var int dK_Mega;
               dK_Mega = Hlp_Random(100);
               // с вероятностью 0.02 подготовить заклинание "Огненный дождь"
               if      (dK_Mega <= 2)  {B_ReadySpell(slf,SPL_Firerain,SPL_Cost_Firerain);              return TRUE;}
               // с вероятностью 0.03 подготовить заклинание "Шторм"
               else if (dK_Mega <= 5)  {B_ReadySpell(slf,SPL_Thunderstorm,SPL_Cost_Thunderstorm);      return TRUE;}
               // с вероятностью 0.05 подготовить заклинание "Удар молнии"
               else if (dK_Mega <= 10) {B_ReadySpell(slf,SPL_LightningFlash,SPL_Cost_LightningFlash);  return TRUE;}
               // с вероятностью 0.9 подготовить заклинание "Малая огненная буря"
               else                    {B_ReadySpell(slf,SPL_Firestorm,SPL_Cost_Firestorm);            return TRUE;};
           }
           // если случайное число == 12
           else if (slf.aivar[AIV_SelectSpell] == 12)
           {
               // случайное число == 0
               slf.aivar[AIV_SelectSpell] = 0;
           };
       };
       return TRUE;
   };
   // если агрессор Маг огня или агрессор всегда использует магию
   if(slf.guild == GIL_KDF) || (slf.aivar[AIV_MagicUser] == MAGIC_ALWAYS)
   {
       // создать в инвентаре агрессора следующие руны, если он их не имеет
       if(Npc_HasItems(slf,ItRu_Concussionbolt) == 0)
       {
           CreateInvItems(slf,ItRu_Concussionbolt,1);      // "Concussionbolt" (Название не переведено)
       };
       if(Npc_HasItems(slf,ItRu_InstantFireBall) == 0)
       {
           CreateInvItems(slf,ItRu_InstantFireBall,1);     // "Огненный шар"
       };
       if(Npc_HasItems(slf,ItRu_Deathball) == 0)
       {
           CreateInvItems(slf,ItRu_Deathball,1);           // "Deathball" (Название не переведено)
       };
       if(Npc_HasItems(slf,ItRu_FullHeal) == 0)
       {
           CreateInvItems(slf,ItRu_FullHeal,1);            // "Лечить сильное ранение"
       };
       // если текущая жизнь агрессора < 100 (Примечание: здесь и далее необходимо self заменить на slf)
       if(self.attribute[ATR_HITPOINTS] < 100)
       {
           // подготовить заклинание "Лечить сильное ранение"
           B_ReadySpell(slf,SPL_FullHeal,SPL_Cost_FullHeal);
           return TRUE;
       }
       // если причина атаки агрессора убийство цели
       else if(C_NpcHasAttackReasonToKill(self))
       {
           // если цель неуязвима или цель Равен (Примечание: Ошибка - необходимо self и other заменить на oth)
           if(self.flags == NPC_FLAG_IMMORTAL) || (Hlp_GetInstanceID(other) == Hlp_GetInstanceID(Raven))
           {
               // подготовить заклинание "Deathball"
               B_ReadySpell(slf,SPL_Deathball,SPL_Cost_Deathball);
           }
           else
           {
               // подготовить заклинание "Огненный шар"
               B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireBall);
           };
           return TRUE;
       }
       else
       {
           // подготовить заклинание "Concussionbolt"
           B_ReadySpell(slf,SPL_Concussionbolt,SPL_Cost_Concussionbolt);
           return TRUE;
       };
   };
   // если агрессор Паладин
   if(slf.guild == GIL_PAL)
   {
       // если агрессор применяет тактику сражения охраны крепостных стен
       if(slf.fight_tactic == FAI_NAILED)
       {
           return FALSE;
       };
       // если агрессор не имеет руны "Святая стрела"
       if(Npc_HasItems(slf,ItRu_PalHolyBolt) == 0)
       {
           // создать в инвентаре агрессора руну "Святая стрела"
           CreateInvItems(slf,ItRu_PalHolyBolt,1);
       };
       // если расстояние между агрессором и целью > дистанции атаки оружием ближнего радиуса действия и цель является источником зла
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_MELEE) && (C_NpcIsEvil(oth))
       {
           // подготовить заклинание "Святая стрела"
           B_ReadySpell(slf,SPL_PalHolyBolt,SPL_Cost_PalHolyBolt);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Маг скелетов
   if(slf.guild == GIL_SKELETON_MAGE)
   {
       // создать в инвентаре агрессора следующие руны, если он их не имеет
       if(Npc_HasItems(slf,ItRu_SumSkel) == 0)
       {
           CreateInvItems(slf,ItRu_SumSkel,1);     // "Вызвать скелет"
       };
       if(Npc_HasItems(slf,ItRu_IceCube) == 0)
       {
           CreateInvItems(slf,ItRu_IceCube,1);     // "Ледяная глыба"
       };
       if(Npc_HasItems(slf,ItRu_Icebolt) == 0)
       {
           CreateInvItems(slf,ItRu_Icebolt,1);     // "Ледяная стрела"
       };
       // если случайное число >= 6
       if(slf.aivar[AIV_SelectSpell] >= 6)
       {
           // случайное число = 1
           slf.aivar[AIV_SelectSpell] = 1;
       };
       // если цель не заморожена и случайное число == 0
       if(!Npc_IsInState(oth,ZS_MagicFreeze)) && (slf.aivar[AIV_SelectSpell] == 0)
       {
           // подготовить заклинание "Ледяная глыба"
           B_ReadySpell(slf,SPL_IceCube,SPL_Cost_IceCube);
           return TRUE;
       }
       // если случайное число == 1
       else if (slf.aivar[AIV_SelectSpell] == 1)
       {
           // подготовить заклинание "Вызвать скелет"
           B_ReadySpell(slf,SPL_SummonSkeleton,SPL_Cost_SummonSkeleton);
           return TRUE;
       }
       else
       {
           // подготовить заклинание "Ледяная стрела"
           B_ReadySpell(slf,SPL_Icebolt,SPL_Cost_Icebolt);
           return TRUE;
       };
   };
   // если агрессор Ледяной голем
   if(slf.guild == GIL_ICEGOLEM)
   {
       // если агрессор не имеет руны "Ледяная глыба"
       if(Npc_HasItems(slf,ItRu_IceCube) == 0)
       {
           // создать в инвентаре агрессора руну "Ледяная глыба"
           CreateInvItems(slf,ItRu_IceCube,1);
       };
       // если расстояние между агрессором и целью < дистанции атаки оружием ближнего радиуса действия или цель заморожена
       if(Npc_GetDistToNpc(slf,oth) < FIGHT_DIST_MELEE) || Npc_IsInState(oth,ZS_MagicFreeze)
       {
           return FALSE;
       }
       else
       {
           // подготовить заклинание "Ледяная глыба"
           B_ReadySpell(slf,SPL_IceCube,SPL_Cost_IceCube);
           return TRUE;

       };
   };
   // если агрессор Огненный голем
   if(slf.guild == GIL_FIREGOLEM)
   {
       // если агрессор не имеет руны "Огненный шар"
       if(Npc_HasItems(slf,ItRu_InstantFireball) == 0)
       {
           // создать в инвентаре агрессора руну "Огненный шар"
           CreateInvItems(slf,ItRu_InstantFireball,1);
       };
       // если расстояние между агрессором и целью > дистанции атаки оружием ближнего радиуса действия
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_MELEE)
       {
           // подготовить заклинание "Огненный шар"
           B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Болотный дракон
   if(slf.aivar[AIV_MM_REAL_ID] == ID_DRAGON_SWAMP)
   {
       // если агрессор не имеет руны "Огненный шар"
       if(Npc_HasItems(slf,ItRu_InstantFireball) == 0)
       {
           // создать в инвентаре агрессора руну "Огненный шар"
           CreateInvItems(slf,ItRu_InstantFireball,1);
       };
       // если расстояние между агрессором и целью > дистанции применения драконами магии
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_DRAGON_MAGIC)
       {
           // подготовить заклинание "Огненный шар"
           B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Каменный дракон (всё идентично Болотному дракону)
   if(slf.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ROCK)
   {
       if (Npc_HasItems(slf,ItRu_InstantFireball) == 0)
       {
           CreateInvItems(slf,ItRu_InstantFireball,1);
       };
       if (Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_DRAGON_MAGIC)
       {
           B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Огненный дракон (всё идентично Болотному дракону)
   if(slf.aivar[AIV_MM_REAL_ID] == ID_DRAGON_FIRE)
   {
       if(Npc_HasItems(slf,ItRu_InstantFireball) == 0)
       {
           CreateInvItems(slf,ItRu_InstantFireball,1);
       };
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_DRAGON_MAGIC)
       {
           B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Ледяной дракон (всё идентично Болотному дракону)
   if(slf.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)
   {
       if(Npc_HasItems(slf,ItRu_InstantFireball) == 0)
       {
           CreateInvItems(slf,ItRu_InstantFireball,1);
       };
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_DRAGON_MAGIC)
       {
           B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Дракон нежить
   if(slf.aivar[AIV_MM_REAL_ID] == ID_DRAGON_UNDEAD)
   {
       // очистить очередь AI состояний агрессора (Примечание: заменить self на slf)
       Npc_ClearAIQueue(self);
       // если агрессор не имеет руны "Deathball"
       if(Npc_HasItems(slf,ItRu_Deathball) == 0)
       {
           // создать в инвентаре агрессора руну "Deathball"
           CreateInvItems(slf,ItRu_Deathball,1);
       };
       // если расстояние между агрессором и целью > дистанции применения драконами магии
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_DRAGON_MAGIC)
       {
           // подготовить заклинание "Deathball"
           B_ReadySpell(slf,SPL_Deathball,SPL_Cost_Deathball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   // если агрессор Шаман орков (всё идентично Огненному голему)
   if(slf.aivar[AIV_MM_REAL_ID] == ID_ORCSHAMAN)
   {
       if(Npc_HasItems(slf,ItRu_InstantFireball) == 0)
       {
           CreateInvItems(slf,ItRu_InstantFireball,1);
       };
       if(Npc_GetDistToNpc(slf,oth) > FIGHT_DIST_MELEE)
       {
           B_ReadySpell(slf,SPL_InstantFireball,SPL_Cost_InstantFireball);
           return TRUE;
       }
       else
       {
           return FALSE;
       };
   };
   return FALSE;
};

// ******************************************************************
// Действия, оказываемые заклинаниями, на НПС
// ------------------------------------------------------------------
// Данная функция вызывается только экзешником в состоянии НПС реакции на магию
// self - НПС, против которого применено заклинание
// other - НПС, применивший заклинание
// ******************************************************************

const int COLL_DONOTHING            = 0;        // воздействие не оказывается
const int COLL_DOEVERYTHING         = 1<<0;     // полное воздействие
const int COLL_APPLYDAMAGE          = 1<<1;     // только физический урон
const int COLL_APPLYHALVEDAMAGE     = 1<<2;     // половинный урон
const int COLL_APPLYDOUBLEDAMAGE    = 1<<3;     // удвоенный урон
const int COLL_APPLYVICTIMSTATE     = 1<<4;     // в зависимости от состояния жертвы
const int COLL_DONTKILL             = 1<<5;     // не убивает

// =============================================================
// Аргументы:
// -------------------------------------------------------------
// spellType - тип заклинания
// --------------------------------------------------------------------
// Возвращаемое значение:
// Тип оказываемого воздействия на НПС.
// =============================================================

func int C_CanNpcCollideWithSpell(var int spellType)
{
   // если заклинание "Смерч"
   if(spellType == SPL_Whirlwind)
   {
       // если НПС обездвижен или
       if (C_NpcIsDown(self))
       || (C_BodyStateContains(self,BS_SWIM))  // плывет
       || (C_BodyStateContains(self,BS_DIVE))  // или ныряет
       || (self.guild == GIL_STONEGOLEM)       // или Каменный голем
       || (self.guild == GIL_ICEGOLEM)         // или Ледяной голем
       || (self.guild == GIL_FIREGOLEM)        // или Каменный голем
       || (self.guild == GIL_SUMMONED_GOLEM)   // или Вызванный голем
       || (self.guild == GIL_DEMON)            // или Демон
       || (self.guild == GIL_SUMMONED_DEMON)   // или Вызванный демон
       || (self.guild == GIL_TROLL)            // или Троль
       || (self.guild == GIL_SUMMONED_GOLEM)   // повтор (убрать!)
       || (self.guild == GIL_DRAGON)           // или Дракон
       || (self.guild == GIL_STONEGUARDIAN)    // или Каменный страж
       || (self.flags == NPC_FLAG_IMMORTAL)    // или неубиваем
       || (self.guild == GIL_SHADOWBEAST)      // или Мракорис
       || (self.guild == GIL_GARGOYLE)         // или Огненный дух мракориса
       {
           return COLL_DONOTHING;
       };
       // если НПС охраняет ворота
       if(C_NpcIsGateGuard(self) == TRUE)
       {
           return COLL_APPLYDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Ледяное копье"
   if(spellType == SPL_Icelance)
   {
       // если НПС обездвижен или плывет или ныряет
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           return COLL_DONOTHING;
       };
       // если НПС Ледяной голем или Ледяной волк или Ледяной дракон
       if(self.guild == GIL_ICEGOLEM) || (self.aivar[AIV_MM_REAL_ID] == ID_Icewolf) || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)
       {
           return COLL_APPLYHALVEDAMAGE;
       };
       // если НПС Огненный голем или
       if (self.guild == GIL_FIREGOLEM)
       || (self.aivar[AIV_MM_REAL_ID] == ID_FIREWARAN)     // Огненная ящерица
       || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_FIRE)   // или Огненный дракон
       || (self.guild == GIL_GARGOYLE)                     // или Огненный дух мракориса
       {
           return COLL_APPLYDOUBLEDAMAGE;
       };
       return COLL_APPLYDAMAGE;
   };
   // если заклинание "Шторм"
   if (spellType == SPL_Thunderstorm)
   {
       // если НПС обездвижен или плывет или ныряет
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           return COLL_DONOTHING;
       };
       // если НПС Ледяной голем или Ледяной волк или Ледяной дракон
       if(self.guild == GIL_ICEGOLEM) || (self.aivar[AIV_MM_REAL_ID] == ID_Icewolf) || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)
       {
           return COLL_APPLYHALVEDAMAGE;
       };
       // если НПС Огненный голем или
       if (self.guild == GIL_FIREGOLEM)
       || (self.aivar[AIV_MM_REAL_ID] == ID_FIREWARAN)     // Огненная ящерица
       || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_FIRE)   // или Огненный дракон
       || (self.guild == GIL_GARGOYLE)                     // или Огненный дух мракориса
       {
           return COLL_APPLYDOUBLEDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Гейзер"
   if(spellType == SPL_Geyser)
   {
       // если НПС обездвижен
       if (C_NpcIsDown(self))
       || (C_BodyStateContains(self,BS_SWIM))  // или плывет
       || (C_BodyStateContains(self,BS_DIVE))  // или ныряет
       || (self.guild == GIL_STONEGOLEM)       // или Каменный голем
       || (self.guild == GIL_ICEGOLEM)         // или Ледяной голем
       || (self.guild == GIL_FIREGOLEM)        // или Огненный голем
       || (self.guild == GIL_SUMMONED_GOLEM)   // или Вызванный голем
       || (self.guild == GIL_DEMON)            // или Демон
       || (self.guild == GIL_SUMMONED_DEMON)   // или Вызванный демон
       || (self.guild == GIL_TROLL)            // или Троль
       || (self.guild == GIL_SUMMONED_GOLEM)   // повтор (убрать!)
       || (self.guild == GIL_DRAGON)           // или Дракон
       || (self.guild == GIL_STONEGUARDIAN)    // или Каменный страж
       {
           return COLL_DONOTHING;
       };
       return COLL_APPLYDAMAGE | COLL_DONTKILL;
   };
   // если заклинание "Кулак воды"
   if(spellType == SPL_Waterfist)
   {
       // если НПС обездвижен или плывет или ныряет
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           return COLL_DONOTHING;
       };
       // если НПС Дракон или Троль
       if(self.guild == GIL_DRAGON) || (self.guild == GIL_TROLL)
       {
           return COLL_APPLYHALVEDAMAGE;
       };
       // если НПС Огненный голем или Огненная ящерица или Огненный дух мракориса
       if(self.guild == GIL_FIREGOLEM) || (self.aivar[AIV_MM_REAL_ID] == ID_FIREWARAN) || (self.guild == GIL_GARGOYLE)
       {
           return COLL_APPLYDOUBLEDAMAGE;
       };
       return COLL_APPLYDAMAGE | COLL_DONTKILL;
   };
   // если заклинание "Шар энергии"
   if(spellType == SPL_EnergyBall)
   {
       // если НПС обездвижен или плывет или ныряет
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           return COLL_DONOTHING;
       };
       // если НПС нежить
       if(C_NpcIsUndead(self))
       {
           return COLL_APPLYHALVEDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Украсть энергию"
   if(spellType == SPL_SuckEnergy)
   {
       // если НПС обездвижен или
       if (C_NpcIsDown(self))
       || (C_BodyStateContains(self,BS_SWIM))      // плывет
       || (C_BodyStateContains(self,BS_DIVE))      // или ныряет
       || (self.guild > GIL_SEPERATOR_HUM)         // или монстр
       || (self.flags == NPC_FLAG_IMMORTAL)        // или неубиваем
       || (Npc_GetDistToNpc(self,other) > 1000)    // или расстояние между НПС > 10м
       || (self.guild == GIL_DMT)                  // или Черный маг
       {
           return COLL_DONOTHING;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Зеленые щупальца"
   if(spellType == SPL_GreenTentacle)
   {
       // если НПС обездвижен или
       if (C_NpcIsDown(self))
       || (C_BodyStateContains(self,BS_SWIM))                  // плывет
       || (C_BodyStateContains(self,BS_DIVE))                  // или ныряет
       || (C_NpcIsGateGuard(self)== TRUE)                      // или охраняет ворота
       || (self.guild == GIL_BLOODFLY)                         // или Кровяной шершень
       || (self.guild == GIL_DEMON)                            // или Демон
       || (self.guild == GIL_TROLL)                            // или Троль
       || (self.guild == GIL_DRAGON)                           // или Дракон
       || (self.guild == GIL_HARPY)                            // или Гарпия
       || (self.aivar[AIV_MM_REAL_ID] == ID_SKELETON_MAGE)     // или Маг скелетов
       || (self.guild == GIL_Gargoyle)                         // или Огненный дух мракориса
       {
           return COLL_DONOTHING;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Рой"
   if(spellType == SPL_Swarm)
   {
       // если НПС обездвижен или
       if (C_NpcIsDown(self))
       || (C_BodyStateContains(self,BS_SWIM))  // плывет
       || (C_BodyStateContains(self,BS_DIVE))  // или ныряет
       || (self.guild == GIL_STONEGOLEM)       // или Каменный голем
       || (self.guild == GIL_ICEGOLEM)         // или Ледяной голем
       || (self.guild == GIL_FIREGOLEM)        // или Огненный голем
       || (self.guild == GIL_SUMMONED_GOLEM)   // или Вызванный голем
       || (self.guild == GIL_DEMON)            // или Демон
       || (self.guild == GIL_SUMMONED_DEMON)   // или Вызванный демон
       || (self.guild == GIL_TROLL)            // или Троль
       || (self.guild == GIL_BLOODFLY)         // или Кровяной шершень
       || (self.guild == GIL_DRAGON)           // или Дракон
       || (self.guild == GIL_Gargoyle)         // или Огненный дух мракориса
       || (self.guild == GIL_DMT)              // или Черный маг
       || (self.guild == GIL_STONEGUARDIAN)    // или Каменный страж
       || (C_NPCIsUndead(self) == TRUE)        // или нежить
       {
           return COLL_DONOTHING;
       };
       // если НПС монстр или охраняет ворота
       if(self.guild > GIL_SEPERATOR_HUM) || (C_NpcIsGateGuard(self) == TRUE)
       {
           return COLL_APPLYDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Крик мертвых"
   if(spellType == SPL_Skull)
   {
       // если НПС обездвижен или плывет или ныряет или нежить
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE)) || (C_NpcIsUndead(self))
       {
           return COLL_DONOTHING;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Порыв ветра"
   if(spellType == SPL_WINDFIST)
   {
       // если расстояние между НПС > 10м
       if(Npc_GetDistToNpc(other,self) >= 1000)
       {
           return COLL_DONOTHING;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание "Молния" или "Шаровая молния" или "Стрела сотрясения"
   if(spellType == SPL_Zap) || (spellType == SPL_ChargeZap) || (spellType == SPL_Concussionbolt)
   {
       // если НПС обездвижен
       if(C_NpcIsDown(self))
       {
           return COLL_DONOTHING;
       };
       return COLL_APPLYDAMAGE | COLL_DONTKILL;
   };
   // если нападающий Черный маг
   if(other.guild == GIL_DMT)
   {
       // если заклинание "Огненный дождь" или "Шторм" или "Удар молнии"
       if(spellType == SPL_Firerain) || (spellType == SPL_Thunderstorm) || (spellType == SPL_LightningFlash)
       {
           // если НПС Черный маг
           if(self.guild == GIL_DMT)
           {
               return COLL_DONOTHING;
           }
           // если НПС ГГ
           else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(hero))
           {
               return COLL_APPLYHALVEDAMAGE;
           };
       };
       // если заклинание Большая огненная буря и НПС Черный маг
       if(spellType == SPL_Firestorm) && (self.guild == GIL_DMT)
       {
           return COLL_DONOTHING;
       };
   };
   // если заклинание Большой огненный шар или
   if (spellType == SPL_ChargeFireball)
   || (spellType == SPL_InstantFireball)   // или Огненный шар
   || (spellType == SPL_Firerain)          // или Огненный дождь
   || (spellType == SPL_Firebolt)          // или Огненная стрела
   || (spellType == SPL_Firestorm)         // или Большая огненная буря
   || (spellType == SPL_Pyrokinesis)       // или Малая огненная буря
   || (spellType == SPL_Deathbolt)         // или Стрела смерти
   || (spellType == SPL_Deathball)         // или Шар смерти
   {
       // если НПС обездвижен или плывет или ныряет
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           return COLL_DONOTHING;
       };
       // если НПС Огненный голем или
       if (self.guild == GIL_FIREGOLEM)
       || (self.aivar[AIV_MM_REAL_ID] == ID_FIREWARAN)     // Огненная ящерица
       || (self.guild == GIL_GARGOYLE)                     // или Огненный дух мракориса
       || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_FIRE)   // или Огненный дракон
       {
           return COLL_APPLYHALVEDAMAGE;
       }
       // если НПС Ледяной голем или
       if (self.guild == GIL_ICEGOLEM)
       || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)    // Ледяной дракон
       || (self.aivar[AIV_MM_REAL_ID] == ID_ICEWOLF)       // или Ледяной волк
       {
           return COLL_APPLYDOUBLEDAMAGE;
       };
       // если НПС Каменный голем или
       if (self.guild == GIL_STONEGOLEM)
       || (self.guild == GIL_SUMMONED_GOLEM)   // Вызванный голем
       || (self.guild == GIL_DEMON)            // или Демон
       || (self.guild == GIL_SUMMONED_DEMON)   // или Вызванный демон
       || (self.guild == GIL_TROLL)            // или Троль
       || (self.guild == GIL_DRAGON)           // или Дракон
       {
           return COLL_APPLYDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание Ледяная глыба или Ледяная волна или Ледяная стрела
   if(spellType  == SPL_IceCube) || (spellTYpe  == SPL_IceWave) || (spelltype  == SPL_Icebolt)
   {
       // если НПС обездвижен или плывет или ныряет
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           return COLL_DONOTHING;
       };
       // если НПС Огненный голем или
       if (self.guild == GIL_FIREGOLEM)
       || (self.aivar[AIV_MM_REAL_ID] == ID_FIREWARAN)     // Огненная ящерица
       || (self.guild == GIL_GARGOYLE)                     // или Огненный дух мракориса
       || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_FIRE)   // или Огненный дракон
       {
           return COLL_APPLYDOUBLEDAMAGE;
       };
       // если НПС Ледяной голем или
       if (self.guild == GIL_ICEGOLEM)
       || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)    // Ледяной дракон
       || (self.aivar[AIV_MM_REAL_ID] == ID_ICEWOLF)       // или Ледяной волк
       {
           return COLL_APPLYHALVEDAMAGE;
       };
       // если НПС Каменный голем или
       if (self.guild == GIL_STONEGOLEM)
       || (self.guild == GIL_SUMMONED_GOLEM)   // Вызванный голем
       || (self.guild == GIL_DEMON)            // или Демон
       || (self.guild == GIL_SUMMONED_DEMON)   // или Вызванный демон
       || (self.guild == GIL_TROLL)            // или Троль
       || (self.guild == GIL_DRAGON)           // или Дракон
       {
           return COLL_APPLYDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание Удар молнии
   if (spellType == SPL_LightningFlash)
   {
       // если НПС обездвижен
       if(C_NpcIsDown(self))
       {
           return COLL_DONOTHING;
       };
       // если НПС плывет или ныряет
       if(C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE))
       {
           COLL_APPLYDOUBLEDAMAGE;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание Страх
   if(spellType == SPL_Fear)
   {
       // если НПС не Огненный голем и
       if (self.guild != GIL_FIREGOLEM)
       && (self.guild != GIL_ICEGOLEM)         // не Ледяной голем
       && (self.guild != GIL_STONEGOLEM)       // и не Каменный голем
       && (self.guild != GIL_SUMMONED_GOLEM)   // и не Вызванный голем
       && (self.guild != GIL_SWAMPSHARK)       // и не Болотный голем
       && (self.guild != GIL_TROLL)            // и не Троль
       && (!C_NpcIsEvil(self))                 // и не источник зла
       && (C_NpcIsGateGuard(self) == FALSE)    // и не охранник ворот
       {
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   };
   // если заклинание Уничтожить нежить
   if(spellType == SPL_DestroyUndead)
   {
       // если НПС нежить и жизнь НПС <= урону заклинания
       if(C_NpcIsUndead(self)) && (self.attribute[ATR_HITPOINTS_MAX] <= SPL_Damage_DESTROYUNDEAD)
       {
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   };
   // если заклинание Дым смерти
   if(spellType == SPL_BreathOfDeath)
   {
       // если расстояние между НПС < 10м и НПС не нежить
       if(Npc_GetDistToNpc(other,self) < 1000) && (!C_NpcIsUndead(self))
       {
           // если НПС дракон или НПС не ГГ
           if(self.guild == GIL_DRAGON) || (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(hero))
           {
               return COLL_APPLYHALVEDAMAGE;
           };
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   };
   // если заклинание Волна смерти
   if(spellType == SPL_MassDeath)
   {
       // если НПС не нежить
       if(!C_NpcIsUndead(self))
       {
           // если НПС дракон
           if(self.guild == GIL_DRAGON)
           {
               return COLL_APPLYHALVEDAMAGE;
           };
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   };
   // если заклинание Святой удар
   if(spellType == SPL_MasterOfDisaster)
   {
       // если НПС не обездвижен и не плывет и не ныряет и нежить
       if(!C_NpcIsDown(self)) && (!C_BodyStateContains(self,BS_SWIM)) && (!C_BodyStateContains(self,BS_DIVE)) && (C_NpcIsEvil(self))
       {
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   };
   // если заклинание Уменьшить монстра
   if(spellType == SPL_Shrink)
   {
       // если НПС обездвижен или плывет или ныряет или дракон
       if(C_NpcIsDown(self)) || (C_BodyStateContains(self,BS_SWIM)) || (C_BodyStateContains(self,BS_DIVE)) || (self.guild == GIL_DRAGON)
       {
           return COLL_DONOTHING;
       };
       return COLL_DOEVERYTHING;
   };
   // если заклинание Святая стрела
   if(spellType == SPL_PalHolyBolt)
   {
       // если НПС источник зла
       if(C_NpcIsEvil(self))
       {
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   }
   // если заклинание Разогнать дьяволов
   else if(spellType == SPL_PalRepelEvil)
   {
       // если НПС источник зла
       if(C_NpcIsEvil(self))
       {
           // если жизнь НПС <= ущербу заклинания
           if(self.attribute[ATR_HITPOINTS_MAX] <= SPL_Damage_PalRepelEvil)
           {
               return COLL_DOEVERYTHING;
           }
           else
           {
               return COLL_APPLYHALVEDAMAGE;
           };
       };
       return COLL_DONOTHING;
   }
   // если заклинание Уничтожить дьяволов
   else if(spellType == SPL_PalDestroyEvil)
   {
       // если НПС источник зла и жизнь НПС <= ущербу заклинания
       if(C_NpcIsEvil(self)) && (self.attribute[ATR_HITPOINTS_MAX] <= SPL_Damage_PalDestroyEvil)
       {
           return COLL_DOEVERYTHING;
       };
       return COLL_DONOTHING;
   };
   return COLL_DOEVERYTHING;
};

//**************************************************************
//****************
// Управление процессом инвестирования маны для всех заклинаний
//------------------------------------------------------------------------------
// Данная функция вызывается только экзешником
// self - кастующий НПС (агрессор)
// other - НПС цель
//******************************************************************************
//=============================================================
// Аргументы:
//-------------------------------------------------------------
// manaInvested - кол-во инвестируемой маны
// -------------------------------------------------------------
// Возвращаемое значение:
// Одна из констант, управляющая процессом инвестирования маны.
// =============================================================

func int Spell_ProcessMana(var int manaInvested)
{
   var int activeSpell;
   // получить активное заклинание агрессора
   activeSpell = Npc_GetActiveSpell(self);
   // В зависимости от заклинания вызывается соответствующая функция, управляющая инвестированием маны
   if(activeSpell == SPL_PalLight          )   {   return  Spell_Logic_PalLight        (manaInvested); };
   if(activeSpell == SPL_PalLightHeal      )   {   return  Spell_Logic_PalLightHeal    (manaInvested); };
   if(activeSpell == SPL_PalHolyBolt       )   {   return  Spell_Logic_PalHolyBolt     (manaInvested); };
   if(activeSpell == SPL_PalMediumHeal     )   {   return  Spell_Logic_PalMediumHeal   (manaInvested); };
   if(activeSpell == SPL_PalRepelEvil      )   {   return  Spell_Logic_PalRepelEvil    (manaInvested); };
   if(activeSpell == SPL_PalFullHeal       )   {   return  Spell_Logic_PalFullHeal     (manaInvested); };
   if(activeSpell == SPL_PalDestroyEvil    )   {   return  Spell_Logic_PalDestroyEvil  (manaInvested); };
   if(activeSpell == SPL_PalTeleportSecret )   {   return  Spell_Logic_PalTeleportSecret(manaInvested); };
   if(activeSpell == SPL_TeleportSeaport   )   {   return  Spell_Logic_TeleportSeaport (manaInvested); };
   if(activeSpell == SPL_TeleportMonastery )   {   return  Spell_Logic_TeleportMonastery(manaInvested); };
   if(activeSpell == SPL_TeleportFarm      )   {   return  Spell_Logic_TeleportFarm    (manaInvested); };
   if(activeSpell == SPL_TeleportXardas    )   {   return  Spell_Logic_TeleportXardas  (manaInvested); };
   if(activeSpell == SPL_TeleportPassNW    )   {   return  Spell_Logic_TeleportPassNW  (manaInvested); };
   if(activeSpell == SPL_TeleportPassOW    )   {   return  Spell_Logic_TeleportPassOW  (manaInvested); };
   if(activeSpell == SPL_TeleportOC        )   {   return  Spell_Logic_TeleportOC      (manaInvested); };
   if(activeSpell == SPL_TeleportOWDemonTower) {   return  Spell_Logic_TeleportOWDemonTower(manaInvested); };
   if(activeSpell == SPL_TeleportTaverne   )   {   return  Spell_Logic_TeleportTaverne (manaInvested); };
   if(activeSpell == SPL_LIGHT             )   {   return  Spell_Logic_Light           (manaInvested); };
   if(activeSpell == SPL_Firebolt          )   {   return  Spell_Logic_Firebolt        (manaInvested); };
   if(activeSpell == SPL_Icebolt           )   {   return  Spell_Logic_Icebolt         (manaInvested); };
   if(activeSpell == SPL_Zap               )   {   return  Spell_Logic_Zap             (manaInvested); };
   if(activeSpell == SPL_LightHeal         )   {   return  Spell_Logic_LightHeal       (manaInvested); };
   if(activeSpell == SPL_SummonGoblinSkeleton) {   return  Spell_Logic_SummonGoblinSkeleton(manaInvested); };
   if(activeSpell == SPL_InstantFireball   )   {   return  Spell_Logic_InstantFireball (manaInvested); };
   if(activeSpell == SPL_SummonWolf        )   {   return  Spell_Logic_SummonWolf      (manaInvested); };
   if(activeSpell == SPL_WINDFIST          )   {   return  Spell_Logic_Windfist        (manaInvested); };
   if(activeSpell == SPL_Sleep             )   {   return  Spell_Logic_Sleep           (manaInvested); };
   if(activeSpell == SPL_MediumHeal        )   {   return  Spell_Logic_MediumHeal      (manaInvested); };
   if(activeSpell == SPL_LightningFlash    )   {   return  Spell_Logic_LightningFlash  (manaInvested); };
   if(activeSpell == SPL_ChargeFireball    )   {   return  Spell_Logic_ChargeFireball  (manaInvested); };
   if(activeSpell == SPL_ChargeZap         )   {   return  Spell_Logic_ChargeZap       (manaInvested); };
   if(activeSpell == SPL_SummonSkeleton    )   {   return  Spell_Logic_SummonSkeleton  (manaInvested); };
   if(activeSpell == SPL_Fear              )   {   return  Spell_Logic_Fear            (manaInvested); };
   if(activeSpell == SPL_IceCube           )   {   return  Spell_Logic_IceCube         (manaInvested); };
   if(activeSpell == SPL_ChargeZap         )   {   return  Spell_Logic_ChargeZap       (manaInvested); };
   if(activeSpell == SPL_SummonGolem       )   {   return  Spell_Logic_SummonGolem     (manaInvested); };
   if(activeSpell == SPL_DestroyUndead     )   {   return  Spell_Logic_DestroyUndead   (manaInvested); };
   if(activeSpell == SPL_Pyrokinesis       )   {   return  Spell_Logic_Pyrokinesis     (manaInvested); };
   if(activeSpell == SPL_Firestorm         )   {   return  Spell_Logic_Firestorm       (manaInvested); };
   if(activeSpell == SPL_IceWave           )   {   return  Spell_Logic_IceWave         (manaInvested); };
   if(activeSpell == SPL_SummonDemon       )   {   return  Spell_Logic_SummonDemon     (manaInvested); };
   if(activeSpell == SPL_FullHeal          )   {   return  Spell_Logic_FullHeal        (manaInvested); };
   if(activeSpell == SPL_Firerain          )   {   return  Spell_Logic_Firerain        (manaInvested); };
   if(activeSpell == SPL_BreathOfDeath     )   {   return  Spell_Logic_BreathOfDeath   (manaInvested); };
   if(activeSpell == SPL_MassDeath         )   {   return  Spell_Logic_MassDeath       (manaInvested); };
   if(activeSpell == SPL_ArmyOfDarkness    )   {   return  Spell_Logic_ArmyOfDarkness  (manaInvested); };
   if(activeSpell == SPL_Shrink            )   {   return  Spell_Logic_Shrink          (manaInvested); };
   if(activeSpell == SPL_TrfSheep          )   {   return  Spell_Logic_TrfSheep        (manaInvested); };
   if(activeSpell == SPL_TrfScavenger      )   {   return  Spell_Logic_TrfScavenger    (manaInvested); };
   if(activeSpell == SPL_TrfGiantRat       )   {   return  Spell_Logic_TrfGiantRat     (manaInvested); };
   if(activeSpell == SPL_TrfGiantBug       )   {   return  Spell_Logic_TrfGiantBug     (manaInvested); };
   if(activeSpell == SPL_TrfWolf           )   {   return  Spell_Logic_TrfWolf         (manaInvested); };
   if(activeSpell == SPL_TrfWaran          )   {   return  Spell_Logic_TrfWaran        (manaInvested); };
   if(activeSpell == SPL_TrfSnapper        )   {   return  Spell_Logic_TrfSnapper      (manaInvested); };
   if(activeSpell == SPL_TrfWarg           )   {   return  Spell_Logic_TrfWarg         (manaInvested); };
   if(activeSpell == SPL_TrfFireWaran      )   {   return  Spell_Logic_TrfFireWaran    (manaInvested); };
   if(activeSpell == SPL_TrfLurker         )   {   return  Spell_Logic_TrfLurker       (manaInvested); };
   if(activeSpell == SPL_TrfShadowbeast    )   {   return  Spell_Logic_TrfShadowbeast  (manaInvested); };
   if(activeSpell == SPL_TrfDragonSnapper  )   {   return  Spell_Logic_TrfDragonSnapper(manaInvested); };
   if(activeSpell == SPL_Charm             )   {   return  Spell_Logic_Charm           (manaInvested); };
   if(activeSpell == SPL_MasterOfDisaster  )   {   return  Spell_Logic_MasterOfDisaster(manaInvested); };
   if(activeSpell == SPL_ConcussionBolt    )   {   return  Spell_Logic_ConcussionBolt  (manaInvested); };
   if(activeSpell == SPL_Deathbolt         )   {   return  Spell_Logic_Deathbolt       (manaInvested); };
   if(activeSpell == SPL_Deathball         )   {   return  Spell_Logic_Deathball       (manaInvested); };
   if(activeSpell == SPL_Thunderstorm      )   {   return  Spell_Logic_Thunderstorm    (manaInvested); };
   if(activeSpell == SPL_Waterfist         )   {   return  Spell_Logic_Waterfist       (manaInvested); };
   if(activeSpell == SPL_Whirlwind         )   {   return  Spell_Logic_Whirlwind       (manaInvested); };
   if(activeSpell == SPL_Geyser            )   {   return  Spell_Logic_Geyser          (manaInvested); };
   if(activeSpell == SPL_Inflate           )   {   return  Spell_Logic_Inflate         (manaInvested); };
   if(activeSpell == SPL_Icelance          )   {   return  Spell_Logic_Icelance        (manaInvested); };
   if(activeSpell == SPL_Swarm             )   {   return  Spell_Logic_Swarm           (manaInvested); };
   if(activeSpell == SPL_Greententacle     )   {   return  Spell_Logic_Greententacle   (manaInvested); };
   if(activeSpell == SPL_SummonGuardian    )   {   return  Spell_Logic_SummonGuardian  (manaInvested); };
   if(activeSpell == SPL_Energyball        )   {   return  Spell_Logic_Energyball      (manaInvested); };
   if(activeSpell == SPL_SuckEnergy        )   {   return  Spell_Logic_SuckEnergy      (manaInvested); };
   if(activeSpell == SPL_Skull             )   {   return  Spell_Logic_Skull           (manaInvested); };
   if(activeSpell == SPL_SummonZombie      )   {   return  Spell_Logic_SummonZombie    (manaInvested); };
   if(activeSpell == SPL_SummonMud         )   {   return  Spell_Logic_SummonMud       (manaInvested); };
   return SPL_SENDSTOP; // (Моя коррекция)
};

//**************************************************************
//****************
//Управление процессом высвобождения инвестированной маны для заклинаний
//------------------------------------------------------------------------------
// Данная функция вызывается только экзешником
// self - кастующий НПС (агрессор)
//******************************************************************************

//=============================================================
// Аргументы:
//-------------------------------------------------------------
// manaInvested - кол-во высвобождаемой маны
//-------------------------------------------------------------
// Возвращаемое значение:
// Одна из констант, управляющая процессом инвестирования маны.
//=============================================================

func int Spell_ProcessMana_Release(var int manaInvested)
{
   var int activeSpell;
   // получить активное заклинание агрессора
   activeSpell = Npc_GetActiveSpell(self);
   // эти заклинания при освобождении маны всегда срабатывают
   if (activeSpell == SPL_Pyrokinesis  )   {   return  SPL_SENDCAST;   };
   if (activeSpell == SPL_ChargeFireball)  {   return  SPL_SENDCAST;   };
   if (activeSpell == SPL_ChargeZap    )   {   return  SPL_SENDCAST;   };
   if (activeSpell == SPL_WINDFIST     )   {   return  SPL_SENDCAST;   };
   // все остальные заклинания завершаются без эффекта
   return SPL_SENDSTOP;
};

// ******************************************************************
// Прототип всех заклинаний (значения по умолчанию)
// ******************************************************************

prototype C_Spell_Proto(c_spell)
{
   time_per_mana               = 500;              // Время в мс, требуемое для инвестирования маны на один уровень - 500мс
   damage_per_level            = 1;                // Наносимый урон за уровень заклинания - 1 пункт
   damageType                  = DAM_MAGIC;        // Тип повреждения - магия
   spellType                   = SPELL_BAD;        // Категория заклинания - плохое
   canTurnDuringInvest         = 1;                // Возможность поворота во время инвестирования маны - есть
   canChangeTargetDuringInvest = 1;                // Возможность выбора цели во время инвестирования маны - есть
   isMultiEffect               = 0;                // Возможность мультиэффектов заклинания - нет
   targetCollectAlgo           = TARGET_COLLECT_FOCUS_FALLBACK_NONE; // Константа описания цели - цель визуальный объект в фокусе, при потере фокуса траектория устанавливается заклинанием
   targetCollectType           = TARGET_TYPE_NPCS; // Тип цели заклинания - цель НПС
   targetCollectRange          = 10000;            // Дальность действия - 100м
   targetCollectAzi            = 60;               // Угол азимута - 60 градусов
   targetCollectElev           = 60;               // Угол тангажа - 60 градусов
};


16. Заклинания.

Все файлы заклинаний расположены в директории ..\AI\Magic\Spells\
Все заклинания мы рассматривать не будем, разберем только основные типы и принципы работы.

Все заклинания состоят из следующих частей:
1. Объявление самого заклинания типа instance Spell_ххх(C_Spell_Proto) с инициализацией требуемых переменных, где ххх - название заклинания.
2. Функция, управляющая процессом инвестирования маны int Spell_Logic_ххх(var int manaInvested). Аргумент int manaInvested определяет кол-во инвестированной маны, функция возвращает одну из констант управления процессом инвестирования маны.
3. Функция вызова самого заклинания void Spell_Cast_ххх() (вызывается только экзешником). Имя ххх должно строго соответствовать ххх при объявлении инстанции.
// *******************************************
// Заклинание Свет
// *******************************************

const int SPL_Cost_LIGHT        = 10;   // кол-во требуемой маны

const int SPL_Duration_LIGHT    = 5;    // время действия заклинания в минутах

// объявление заклинания с инициализацией
instance Spell_Light(C_Spell_Proto)
{
   time_per_mana           = 500;
   spelltype               = SPELL_NEUTRAL;
   targetCollectAlgo       = TARGET_COLLECT_NONE;
   targetCollectRange      = 0;
   targetCollectAzi        = 0;
   targetCollectElev       = 0;
};

// функция инвестирования маны
func int Spell_Logic_Light(var int manaInvested)
{
   // если активное заклинание свиток и есть требуемое кол-во маны
   if(Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll))
   {
       // старт заклинания
       return SPL_SENDCAST;
   }
   // иначе, если руна и есть требуемое кол-во маны
   else if (self.attribute[ATR_MANA] >= SPL_Cost_Light)
   {
       // старт заклинания
       return SPL_SENDCAST;
   }
   else // мало маны
   {
       // отмена заклинания
       return SPL_SENDSTOP;
   };
};

// вызов заклинания
func void Spell_Cast_Light()
{
   // если свиток
   if(Npc_GetActiveSpellIsScroll(self))
   {
       // уменьшение маны на стоимость свитков
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
   }
   else // руна
   {
       // уменьшение маны на фактическую стоимость заклинания
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Light;
   };
   // изменить случайное число выбора заклинаний
   self.aivar[AIV_SelectSpell] += 1;
};
По такому принципу работают большинство заклинаний, это:
SPL_PalLight            //Святой свет,                      // файл Spell_Light_Alle.d
SPL_PalLightHeal        //Малое лечение паладина            // файл Spell_Heal_Alle.d
SPL_PalMediumHeal       //Среднее лечение паладина
SPL_PalFullHeal         //Сильное лечение паладина
SPL_LightHeal           //Лечить легкое ранение
SPL_MediumHeal          //Лечить среднее ранение
SPL_FullHeal            //Лечить сильное ранение
SPL_PalHolyBolt         //Святая стрела                     // файл Spell_PalHolyBolt.d
SPL_PalRepelEvil        //Разогнать дьяволов                // файл Spell_PalRepelEvil.d
SPL_PalDestroyEvil      //Уничтожить дьяволов               // файл Spell_PalDestroyEvil.d
SPL_Firebolt            //Огненная стрела                   // файл Spell_FireBolt.d
SPL_Zap                 //Молния                            // файл Spell_Zap.d
SPL_Icebolt             //Ледяная стрела                    // файл Spell_IceBolt.d
SPL_InstantFireball     //Огненный шар                      // файл Spell_InstantFireball.d
SPL_LightningFlash      //Удар молнии                       // файл Spell_LightningFlash.d
SPL_IceCube             //Ледяная глыба                     // файл Spell_IceCube.d
SPL_DestroyUndead       //Уничтожить нежить                 // файл Spell_DestroyUndead.d
SPL_Firestorm           //Малая огненная буря               // файл Spell_Firestorm.d
SPL_IceWave             //Ледяная волна                     // файл Spell_IceWave.d
SPL_Firerain            //Огненный дождь                    // файл Spell_Firerain.d
SPL_BreathOfDeath       //Дым смерти                        // файл Spell_BreathOfDeath.d
SPL_MassDeath           //Волна смерти                      // файл Spell_MassDeath.d
SPL_MasterOfDisaster    //Святой удар                       // файл Spell_MasterOfDisaster.d
SPL_Deathbolt           //Стрела смерти                     // файл Spell_DeathBolt.d
SPL_Deathball           //Шар смерти                        // файл Spell_DeathBall.d
SPL_ConcussionBolt      //Стрела сотрясения                 // файл Spell_ConcussionBolt.d
SPL_Thunderstorm        //Шторм                             // файл Spell_Thunderstorm.d
SPL_Whirlwind           //Смерч                             // файл Spell_Whirlwind.d
SPL_WaterFist           //Кулак воды                        // файл Spell_WaterFist.d
SPL_IceLance            //Ледяное копье                     // файл Spell_IceLance.d
SPL_Geyser              //Гейзер                            // файл Spell_Geyser.d
SPL_Swarm               //Рой                               // файл Spell_Swarm.d
SPL_GreenTentacle       //Зеленые щупальца                  // файл Spell_GreenTentacle.d
SPL_Energyball          //Шар энергии                       // файл Spell_EnergyBall.d
SPL_SuckEnergy          //Украсть энергию                   // файл Spell_SuckEnergy.d
SPL_Skull               //Крик мертвых                      // файл Spell_Skull.d

Телепортация.

// *************************
// Заклинания телепортации
// *************************

// стоимость заклинаний
const int SPL_Cost_Teleport     = 10;

// Вывод сообщения о недоступности телепорта
// -------------------------------------------------------
// Level - уровень на котором находится телепорт прибытия
func void B_PrintTeleportTooFarAway(var int Level)
{
   // если телепортация на другой уровень
   if(Level != CurrentLevel)
   {
       // вывод сообщения "Слишком далеко"
       PrintScreen(PRINT_TeleportTooFarAway,-1,YPOS_LevelUp,FONT_ScreenSmall,2);
   };
};

// Инициализация заклинаний
instance Spell_Teleport(C_Spell_Proto)
{
   time_per_mana           = 0;
   spelltype               = SPELL_NEUTRAL;
   targetCollectAlgo       = TARGET_COLLECT_CASTER;
   canTurnDuringInvest     = 0;
   targetCollectRange      = 0;
   targetCollectAzi        = 0;
   targetCollectElev       = 0;
};

// Функции инвестирования маны для всех заклинаний телепортации идентичны ранее рассмотренной
func int Spell_Logic_PalTeleportSecret(var int manaInvested)
{
   if (Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll))
   {
       return SPL_SENDCAST;
   }
   else if (self.attribute[ATR_MANA] >= SPL_Cost_Teleport)
   {
       return SPL_SENDCAST;
   };
   return SPL_NEXTLEVEL;
};

// Функции вызова заклинаний телепортации
func void Spell_Cast_PalTeleportSecret()
{
   // вывод сообщения о недоступности телепорта
   B_PrintTeleportTooFarAway(NEWWORLD_ZEN);
   if (Npc_GetActiveSpellIsScroll(self))
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
   }
   else
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Teleport;
   };
   // телепортация в нужную точку
   AI_Teleport(self,"NW_PAL_SECRETCHAMBER");
   // анимация телепортации
   AI_PlayAni(self,"T_HEASHOOT_2_STAND" );
};

// Остальные функции вызова заклинаний телепортации идентичны приведенной

// Вызов соответствующей функции телепортации в зависимости от активного заклинания
func void Spell_Cast_Teleport()
{
   if (Npc_GetActiveSpell(self) == SPL_PalTeleportSecret   )   {   Spell_Cast_PalTeleportSecret    (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportSeaport     )   {   Spell_Cast_TeleportSeaport      (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportMonastery   )   {   Spell_Cast_TeleportMonastery    (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportFarm        )   {   Spell_Cast_TeleportFarm         (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportXardas      )   {   Spell_Cast_TeleportXardas       (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportPassNW      )   {   Spell_Cast_TeleportPassNW       (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportPassOW      )   {   Spell_Cast_TeleportPassOW       (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportOC          )   {   Spell_Cast_TeleportOC           (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportOWDemonTower)   {   Spell_Cast_TeleportOWDemonTower (); };
   if (Npc_GetActiveSpell(self) == SPL_TeleportTaverne     )   {   Spell_Cast_TeleportTaverne      (); };
};

Заклинания вызова монстров

Рассмотрим их на примере вызова Волка:
// **************
// Вызов Волка
// **************

// стоимость заклинания
const int SPL_Cost_SummonWolf   = 40;

// Инициализация заклинания
instance Spell_SummonWolf(C_Spell_Proto)
{
   time_per_mana           = 0;
   targetCollectAlgo       = TARGET_COLLECT_NONE;
};

// Функция инвестирования маны идентична ранее рассмотренным
func int Spell_Logic_SummonWolf(var int manaInvested)
{
   if(Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll))
   {
       return SPL_SENDCAST;
   }
   else if(self.attribute[ATR_MANA] >= SPL_Cost_SummonWolf)
   {
       return SPL_SENDCAST;
   }
   else
   {
       return SPL_SENDSTOP;
   };

};

// Вызов заклинания
func void Spell_Cast_SummonWolf()
{
   if(Npc_GetActiveSpellIsScroll(self))
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
   }
   else
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_SummonWolf;
   };
   // если заклинание применяет ГГ
   if(Npc_IsPlayer(self))
   {
       // рождение Вызванного волка (1 штука на 5 минут)
       Wld_SpawnNpcRange(self,Summoned_Wolf,1,500);
   }
   else
   {
       // рождение Волка
       Wld_SpawnNpcRange(self,Wolf,1,500);
   };
   self.aivar[AIV_SelectSpell] += 1;
};
По такому принципу работают заклинания:
SPL_SummonGoblinSkeleton    //Вызвать скелет гоблина        // файл Spell_SummonGoblinSkeleton.d
SPL_SummonSkeleton          //Вызвать скелет                // файл Spell_SummonSkeleton.d
SPL_SummonGolem             //Вызвать голема                // файл Spell_SummonGolem.d
SPL_SummonDemon             //Вызвать демона                // файл Spell_SummonDemon.d
SPL_ArmyOfDarkness          //Армия мрака                   // файл Spell_ArmyOfDarkness.d
SPL_SummonGuardian          //Создать стража                // файл Spell_SummonGuardian.d
SPL_SummonZombie            //Создать зомби                 // файл Spell_SummonZombie.d
SPL_SummonMud               //Вызвать Муда                  // файл Spell_SummonMud.d

Заклинания, требующие несколько уровней инвестирования маны

Рассмотрим их на примере заклинания "Порыв ветра":
// ************************
// Заклинание Порыв ветра
// ************************

const int SPL_Cost_WindFist     = 80;   // полная стоимость заклинания
const int STEP_WindFist         = 20;   // стоимость одного уровня заклинания
const int SPL_Damage_WindFist   = 50;   // ущерб наносимый заклинанием на уровень

// объявление заклинания с инициализацией
instance Spell_WindFist(C_Spell_Proto)
{
   time_per_mana           = 30;
   damage_per_level        = SPL_Damage_WindFist;
   damageType              = DAM_FLY;
   canTurnDuringInvest     = TRUE;
   targetCollectAlgo       = TARGET_COLLECT_FOCUS_FALLBACK_NONE;
   targetCollectRange      = 1000;
   targetCollectType       = TARGET_TYPE_NPCS;
};

// функция инвестирования маны
func int Spell_Logic_WindFist(var int manaInvested)
{
   // если недостаточно маны на уровень заклинания
   if(self.attribute[ATR_MANA] < STEP_WindFist)
   {
       // инвестирование маны невозможно
       return SPL_DONTINVEST;
   };
   // если инвестировано маны < 1 уровня
   if(manaInvested <= STEP_WindFist*1)
   {
       // установить уровень 1
       self.aivar[AIV_SpellLevel] = 1;
       // инвестированной маны недостаточно для перехода на следующий уровень
       return SPL_STATUS_CANINVEST_NO_MANADEC;
   }
   // если инвестировано маны > требуемой для 1 уровня и уровень не выше первого
   else if(manaInvested > (STEP_WindFist*1)) && (self.aivar[AIV_SpellLevel] <= 1)
   {
       // уменьшение маны на уровень
       self.attribute[ATR_MANA] = (self.attribute[ATR_MANA] - STEP_WindFist);
       // если мана < 0
       if(self.attribute[ATR_MANA] < 0)
       {
           // мана = 0
           self.attribute[ATR_MANA] = 0;
       };
       // установить уровень 2
       self.aivar[AIV_SpellLevel] = 2;
       // переход на следующий уровень
       return SPL_NEXTLEVEL;
   }
   // аналогично приведенному выше для следующих уровней заклинания
   else if(manaInvested > (STEP_WindFist*2)) && (self.aivar[AIV_SpellLevel] <= 2)
   {
       self.attribute[ATR_MANA] = (self.attribute[ATR_MANA] - STEP_WindFist);
       if(self.attribute[ATR_MANA] < 0)
       {
           self.attribute[ATR_MANA] = 0;
       };
       self.aivar[AIV_SpellLevel] = 3;
       return SPL_NEXTLEVEL;
   }
   else if(manaInvested > (STEP_WindFist*3)) && (self.aivar[AIV_SpellLevel] <= 3)
   {
       self.attribute[ATR_MANA] = (self.attribute[ATR_MANA] - STEP_WindFist);
       if(self.attribute[ATR_MANA] < 0)
       {
           self.attribute[ATR_MANA] = 0;
       };
       self.aivar[AIV_SpellLevel] = 4;
       return SPL_NEXTLEVEL;
   }
   // если достигнут последний уровень
   else if(manaInvested > (STEP_WindFist*3)) && (self.aivar[AIV_SpellLevel] == 4)
   {
       // дальнейшее инвестирование маны невозможно
       return SPL_DONTINVEST;
   };
   // инвестированной маны недостаточно для перехода на следующий уровень
   return SPL_STATUS_CANINVEST_NO_MANADEC;
};

// вызов заклинания
// где spellLevel - достигнутый уровень заклинания
func void Spell_Cast_WindFist(var int spellLevel)
{
   // уменьшение маны для последнего уровня
   self.attribute[ATR_MANA] = (self.attribute[ATR_MANA] - STEP_WindFist);
   if(self.attribute[ATR_MANA] < 0)
   {
       self.attribute[ATR_MANA] = 0;
   };
   // изменить случайное число выбора заклинаний
   self.aivar[AIV_SelectSpell] += 1;
};
По такому принципу работают следующие заклинания:
SPL_ChargeFireball      //Большой огненный шар              // файл Spell_ChargeFireball.d
SPL_ChargeZap           //Шаровая молния                    // файл Spell_ChargeZap.d
SPL_Pyrokinesis         //Большая огненная буря             // файл Spell_Pyrokinesis.d

Трансформация

// **************************
// Заклинания трансформации
// **************************

// стоимости заклинаний
const int SPL_Cost_TrfSheep         = 10;
const int SPL_Cost_TrfScavenger     = 10;
const int SPL_Cost_TrfGiantRat      = 10;
const int SPL_Cost_TrfGiantBug      = 10;
const int SPL_Cost_TrfWolf          = 10;
const int SPL_Cost_TrfWaran         = 10;
const int SPL_Cost_TrfSnapper       = 10;
const int SPL_Cost_TrfWarg          = 10;
const int SPL_Cost_TrfFireWaran     = 10;
const int SPL_Cost_TrfLurker        = 10;
const int SPL_Cost_TrfShadowbeast   = 10;
const int SPL_Cost_TrfDragonSnapper = 10;

// общая инициализация для всех заклинаний
instance Spell_Transform(C_Spell_Proto)
{
   time_per_mana           = 0;
   spelltype               = SPELL_NEUTRAL;
   targetCollectAlgo       = TARGET_COLLECT_NONE;
   canTurnDuringInvest     = 0;
};

// Функции инвестирования маны у всех заклинаний трансформации практически одинаковы, рассмотрим одну их них
func int Spell_Logic_TrfSheep(var int manaInvested)
{
   // если заклинание свиток и маны достаточно для свитка или маны достаточно для руны
   if((Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll)))
   || (self.attribute[ATR_MANA] >= SPL_Cost_TrfSheep)
   {
       // уменьшить ману на стоимость заклинания для руны
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_TrfSheep;
       // ссылка на объявление (инстанцию) зверюшки, в которую превращаемся
       Npc_SetActiveSpellInfo(self,Sheep);
       // старт заклинания
       return SPL_SENDCAST;
   }
   else
   {
       // заклинание невозможно
       return SPL_SENDSTOP;
   };
};

Прочие заклинания

// ****************
// Заклинание Сон
// ****************
// self - кастующий НПС
// other - НПС цель

const int SPL_Cost_Sleep    = 30;   // стоимость маны
const int SPL_TIME_Sleep    = 30;   // время сна в сек

// Инициализация заклинания
instance Spell_Sleep(C_Spell_Proto)
{
   time_per_mana           = 0;
   spelltype               = SPELL_NEUTRAL;
   targetCollectAlgo       = TARGET_COLLECT_FOCUS;
};

// Функция инвестирования маны
func int Spell_Logic_Sleep(var int manaInvested)
{
   // если заклинание свиток и маны достаточно для свитка или маны достаточно для руны
   if((Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll)))
   || (self.attribute[ATR_MANA] >= SPL_Cost_Sleep)
   {
       // если свиток
       if(Npc_GetActiveSpellIsScroll(self))
       {
           // израсходовать ману
           self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
       }
       else // руна
       {
           // израсходовать ману
           self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Sleep;
       };
       // если цель не плывет и не ныряет и не обездвижена и
       if (!C_BodyStateContains(other,BS_SWIM)) && (!C_BodyStateContains(other,BS_DIVE)) && (!C_NpcIsDown(other))
       && (other.guild < GIL_SEPERATOR_HUM)        // человек
       && (other.flags != NPC_FLAG_IMMORTAL)       // и уязвима
       && (Npc_GetDistToNpc(self,other) <= 1000)   // и расстояние между кастующим и целью < 10м
       && (other.guild != GIL_KDF)                 // и цель не Маг огня
       && (other.guild != GIL_DMT)                 // и не Черный маг
       && (other.guild != GIL_PAL)                 // и не Паладин
       {
           // очистить очередь AI состояний цели
           Npc_ClearAIQueue(other);
           // очистить восприятия цели
           B_ClearPerceptions(other);
           // перевести цель в состояние Магического сна
           AI_StartState(other,ZS_MagicSleep,0,"");
       };
       return SPL_SENDCAST;
   }
   else
   {
       return SPL_SENDSTOP;
   };
};

// Вызов заклинания
func void Spell_Cast_Sleep()
{
   // изменить случайное число выбора заклинаний
   self.aivar[AIV_SelectSpell] += 1;
};

// *****************************
// Заклинание Уменьшить монстра
// *****************************

const int SPL_Cost_Shrink   = 300; // стоимость маны

// Инициализация заклинания
instance Spell_Shrink(C_Spell_Proto)
{
   time_per_mana           = 0;
   spelltype               = SPELL_NEUTRAL;
   targetCollectAlgo       = TARGET_COLLECT_FOCUS;
   targetCollectRange      = 1000;
};

// Функция инвестирования маны (аналогична ранее рассмотренным)
func int Spell_Logic_Shrink(var int manaInvested)
{
   if(Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll))
   {
       return SPL_SENDCAST;
   }
   else if(self.attribute[ATR_MANA] >= SPL_Cost_Shrink)
   {
       return SPL_SENDCAST;
   }
   else
   {
       return SPL_SENDSTOP;
   };
};

// Вызов заклинания
func void Spell_Cast_Shrink()
{
   // если свиток
   if(Npc_GetActiveSpellIsScroll(self))
   {
       // израсходовать ману для свитка
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
   }
   else
   {
       // израсходовать ману для руны
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Shrink;
   };
   // если цель уязвима и не обездвижена и монстр и не уменьшена
   if(other.flags != NPC_FLAG_IMMORTAL) && (!C_NpcIsUndead(other)) && (other.guild > GIL_SEPERATOR_HUM) && (other.aivar[AIV_MM_ShrinkState] == 0)
   {
       // очистить очередь AI состояний цели
       Npc_ClearAIQueue(other);
       // очистить восприятия цели
       B_ClearPerceptions(other);
       // перевести цель в состояние уменьшения
       AI_StartState(other,ZS_MagicShrink,0,"");
   };
   // изменить случайное число выбора заклинаний
   self.aivar[AIV_SelectSpell] += 1;
};

// *******************
// Заклинание Раздуть
// *******************

const int SPL_Cost_Inflate      = 10;   // стоимость маны
const int SPL_Inflate_Damage    = 5;    // ущерб от заклинания
const int SPL_TIME_Inflate      = 19;   // время действия 19 сек

// Инициализация заклинания
instance Spell_Inflate(C_Spell_Proto)
{
   time_per_mana           = 0;
   targetCollectAlgo       = TARGET_COLLECT_FOCUS;
};

// Функция инвестирования маны
func int Spell_Logic_Inflate(var int manaInvested)
{
   // если заклинание свиток и маны достаточно для свитка или маны достаточно для руны
   if ((Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll)))
   || (self.attribute[ATR_MANA] >= SPL_Cost_Inflate)
   {
       // если свиток
       if(Npc_GetActiveSpellIsScroll(self))
       {
           // израсходовать ману для свитка
           self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
       }
       else
       {
           // израсходовать ману для руны
           self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Inflate;
       };
       // если цель не плывет и не ныряет и не обездвижена и
       if(!C_BodyStateContains(other,BS_SWIM)) && (!C_BodyStateContains(other,BS_DIVE)) && (!C_NpcIsDown(other))
       && (other.guild < GIL_SEPERATOR_HUM)        // человек
       && (other.flags != NPC_FLAG_IMMORTAL)       // и уязвима
       && (Npc_GetDistToNpc(self,other) <= 1000)   // и расстояние между кастующим и целью < 10м
       && (other.guild != GIL_KDF)                 // и цель не Маг огня
       && (other.guild != GIL_DMT)                 // и не Черный маг
       && (other.guild != GIL_PAL)                 // и не Паладин
       {
           // очистить очередь AI состояний цели
           Npc_ClearAIQueue(other);
           // очистить восприятия цели
           B_ClearPerceptions(other);
           // перевести цель в состояние Раздутия
           AI_StartState(other,ZS_Inflate,0,"");
       };
       return SPL_SENDCAST;
   }
   else
   {
       return SPL_SENDSTOP;
   };
};

// Вызов заклинания
func void Spell_Cast_Inflate()
{
   // изменить случайное число выбора заклинаний
   self.aivar[AIV_SelectSpell] += 1;
};

// ******************
// Заклинание Страх
// ******************

const int SPL_Cost_Fear     = 50;   // стоимость маны
const int SPL_TIME_Fear     = 5;    // время действия 5 сек

// Инициализация заклинания
instance Spell_Fear(C_Spell_Proto)
{
   time_per_mana           = 0;
   damage_per_level        = 0;
   targetCollectAlgo       = TARGET_COLLECT_NONE;
};

// Функция инвестирования маны (аналогична рассмотренным)
func int Spell_Logic_Fear(var int manaInvested)
{
   if(Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll))
   {
       return SPL_SENDCAST;
   }
   else if(self.attribute[ATR_MANA] >= SPL_Cost_Fear)
   {
       return SPL_SENDCAST;
   }
   else
   {
       return SPL_SENDSTOP;
   };
};

// Вызов заклинания
func void Spell_Cast_Fear()
{
   // если цель не Дракон
   if(other.guild != GIL_DRAGON)
   {
       // перевести всех НПС, находящихся на расстоянии 10м от кастующего, в состояние страха
       AI_SetNpcsToState(self,ZS_MagicFlee,1000);
   };
   // израсходовать ману (аналогично рассмотренным заклинаниям)
   if(Npc_GetActiveSpellIsScroll(self))
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
   }
   else
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Fear;
   };
   self.aivar[AIV_SelectSpell] += 1;
};

// ******************
// Заклинание Забыть
// ******************

const int SPL_Cost_Charm        = 50;   // стоимость маны
const int SPL_Damage_Charm      = 0;    // причиняемый ущерб

// Инициализация заклинания
instance Spell_Charm(C_Spell_Proto)
{
   time_per_mana           = 0;
   spelltype               = SPELL_NEUTRAL;
   damage_per_level        = SPL_Damage_Charm;
   damageType              = DAM_MAGIC;
};

// Функция инвестирования маны
func int Spell_Logic_Charm(var int manaInvested)
{
   // если заклинание свиток и маны достаточно для свитка или маны достаточно для руны
   if ((Npc_GetActiveSpellIsScroll(self) && (self.attribute[ATR_MANA] >= SPL_Cost_Scroll)))
   || (self.attribute[ATR_MANA] >= SPL_Cost_Charm)
   {
       // если НПС цель видел криминал ГГ и квест Игназа в стадии выполнения
       if(other.aivar[AIV_NpcSawPlayerCommit] != CRIME_NONE) && (MIS_Ignaz_Charm == LOG_RUNNING)
       {
           // тест заклинания проведен
           Charm_Test = TRUE;
       };
       // удалить криминал ГГ для соответствующей локации
       B_DeletePetzCrime(other);
       // очистить что цель видела криминал
       other.aivar[AIV_NpcSawPlayerCommit] = CRIME_NONE;
       // последнего сражения с ГГ не было
       other.aivar[AIV_LastFightAgainstPlayer] = FIGHT_NONE;
       // если гильдии цели и кастующего не враги
       if(Wld_GetGuildAttitude(other.guild,self.guild) != ATT_HOSTILE)
       {
           // если отношение цели и кастующего враждебно
           if(Npc_GetAttitude(other,self) == ATT_HOSTILE)
           {
               // установить отношение цели к кастующему равное отношению их гильдий
               Npc_SetTempAttitude(other,Wld_GetGuildAttitude(other.guild,self.guild));

           };
       };
       return SPL_SENDCAST;
   }
   else
   {
       return SPL_SENDSTOP;
   };
};

// Вызов заклинания (идентичен ранее рассмотренным)
func void Spell_Cast_Charm()
{
   if(Npc_GetActiveSpellIsScroll(self))
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
   }
   else
   {
       self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Charm;
   };
   self.aivar[AIV_SelectSpell] += 1;
};


17. Обработчики состояний заклинаний.

Все обработчики расположены в директории скриптов ..\AI\Magic\ZS_Magic в одноименных файлах.
//****************************************************
// Обработчик состояния заклинания Зеленые щупальца
//****************************************************

// ----------------------------------------------------
// self - жертва заклинания
// ----------------------------------------------------

// Функция реакции на локальное восприятие магии (заклинания)
func void B_RestartGreententacle()
{
   // если последнее примененное заклинание Зеленые щупальца
   if(Npc_GetLastHitSpellID(self) == SPL_Greententacle)
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
   };
};

// Закончить действие заклинания
func void B_StopGreententacle()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если НПС человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
   };
};

// Инициализация состояния
func void ZS_Greententacle()
{
   // установить локальное восприятие на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartGreententacle);
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_AssessDamage);
   // остановить проигрывание файлов анимации заклинания
   Npc_StopAni(self,"s_GreententacleA_Victim");
   Npc_StopAni(self,"s_GreententacleB_Victim");
   Npc_StopAni(self,"s_GreententacleC_Victim");
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp      (self);
   // если НПС находится не в бессознательном состоянии
   if(!C_BodyStateContains(self,BS_UNCONSCIOUS))
   {
       // если жертва человек
       if(self.guild < GIL_SEPERATOR_HUM)
       {
           var int randy;
           //randy = Hlp_Random(3);
           // проигрывается анимация бессознательного состояния (Примечание: т.к. предыдущая строка в скрипте закрыта, то randy всегда имеет значение 0)
           if(randy == 0)  { AI_PlayAniBS(self,"T_STAND_2_GREENTENTACLEA_VICTIM",BS_UNCONSCIOUS); };
           if(randy == 1)  { AI_PlayAniBS(self,"T_STAND_2_GREENTENTACLEB_VICTIM",BS_UNCONSCIOUS); };
           if(randy == 2)  { AI_PlayAniBS(self,"T_STAND_2_GREENTENTACLEC_VICTIM",BS_UNCONSCIOUS); };
       }
       else
       {
           // анимация бессознательного состояния
           AI_PlayAniBS(self,"T_STAND_2_FREEZE_VICTIM",BS_UNCONSCIOUS);
       };
   };
};

// Функция цикла состояния
func int ZS_Greententacle_Loop ()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_TIME_Greententacle)
   {
       // остановить действие заклинания
       B_StopGreententacle();
       return LOOP_END;
   };
   return              LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_GreenTentacle_End()
{
};

// ****************************************
// Обработчик состояния заклинания Раздуть
// ****************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Функция реакции на локальное восприятие магии (заклинания)
func void B_RestartInflate()
{
   // если последнее примененное заклинание Раздуть
   if(Npc_GetLastHitSpellID(self) == SPL_Inflate)
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
   };
};

// Закончить действие заклинания
func void B_StopInflate()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
   };
};

// Инициализация состояния
func void ZS_Inflate()
{
   // установить локальное восприятие на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartInflate);
   // остановить проигрывание файла анимации заклинания
   Npc_StopAni(self,"S_INFLATE_VICTIM");
   // если НПС находится не в бессознательном состоянии
   if(!C_BodyStateContains(self,BS_UNCONSCIOUS))
   {
       // анимация бессознательного состояния
       AI_PlayAniBS(self,"T_STAND_2_INFLATE_VICTIM",BS_UNCONSCIOUS);
   };
   // сброс времени цикла
   self.aivar[AIV_InflateStateTime] = 0;
};

// Функция цикла состояния
func int ZS_Inflate_Loop ()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_TIME_Inflate)
   {
       // остановить действие заклинания
       B_StopInflate();
       return LOOP_END;
   };
   // если текущее время состояния не равно времени цикла
   if(Npc_GetStateTime(self) != self.aivar[AIV_InflateStateTime])
   {
       // в зависимости от времени состояния установить жирность (объем) фигуры жертвы
       // как видим по коэффициентам объема сначала идет раздутие фигуры, а затем возвращение к нормальному состоянию
       if(Npc_GetStateTime(self) == 1)       {  Mdl_SetModelFatness(self,1);   }
       else if(Npc_GetStateTime(self) == 2)  {  Mdl_SetModelFatness(self,3);   }
       else if(Npc_GetStateTime(self) == 3)  {  Mdl_SetModelFatness(self,8 );   }
       else if(Npc_GetStateTime(self) == 4)  {  Mdl_SetModelFatness(self,12);  }
       else if(Npc_GetStateTime(self) == 5)
       {
           Mdl_SetModelFatness(self,9);
           // жертва говорит агрессору (SVM фраза) "Ууух!"
           B_Say(self,other,"$Aargh_1");
       }
       else if(Npc_GetStateTime(self) == 6)  {  Mdl_SetModelFatness (self,15); }
       else if(Npc_GetStateTime(self) == 7)  {  Mdl_SetModelFatness (self,18 ); }
       else if(Npc_GetStateTime(self) == 8 )
       {
           Mdl_SetModelFatness(self,11);
           // жертва говорит агрессору (SVM фраза) "Ууух!"
           B_Say (self, other, "$Aargh_2");
       }
       else if(Npc_GetStateTime(self) == 9)  {  Mdl_SetModelFatness(self,15);  }
       else if(Npc_GetStateTime(self) == 10) {  Mdl_SetModelFatness(self,13);  }
       else if(Npc_GetStateTime(self) == 11) {  Mdl_SetModelFatness(self,12);  }
       else if(Npc_GetStateTime(self) == 12) {  Mdl_SetModelFatness(self,10);  }
       else if(Npc_GetStateTime(self) == 13) {  Mdl_SetModelFatness(self,8 );   }
       else if(Npc_GetStateTime(self) == 14)
       {
           Mdl_SetModelFatness(self,5);
           // жертва говорит агрессору (SVM фраза) "Ууух!"
           B_Say(self,other,"$Aargh_3");
       }
       else if(Npc_GetStateTime(self) == 15)  {  Mdl_SetModelFatness(self,6);  }
       else if(Npc_GetStateTime(self) == 16)  {  Mdl_SetModelFatness(self,4);  }
       else if(Npc_GetStateTime(self) == 17)  {  Mdl_SetModelFatness(self,2);  }
       else if(Npc_GetStateTime(self) == 18 )  {  Mdl_SetModelFatness(self,1);  }
       else if(Npc_GetStateTime(self) == 19)  {  Mdl_SetModelFatness(self,1);  };
       // установить время уикла на текущее время
       self.aivar[AIV_InflateStateTime] = Npc_GetStateTime(self);
       // если жизнь жертвы > ущерба заклинания
       if(self.attribute[ATR_HITPOINTS] > SPL_Inflate_DAMAGE)
       {
           // нанести ужерб жертве
           B_MagicHurtNpc(other,self,SPL_Inflate_DAMAGE);
       }
       else
       {
           // нанести полный ужерб жертве (смерть)
           B_MagicHurtNpc(other,self,self.attribute[ATR_HITPOINTS] - 1);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Inflate_End()
{
};

// ***********************************************************
// Обработчик состояния магического горения
// -----------------------------------------------------------
// В это состояние НПС переводится экзешником, а не скриптами
// ***********************************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

const int SPL_MAGICBURN_DAMAGE_PER_SEC = 1; // ущерб в сек от заклинания

// Закончить действие заклинания
func void B_StopMagicBurn()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
       // продолжить распорядок дня НПС (т.к. не было принудительного перевода НПС в это состояние из скриптов и завершение этого состояния выполняется не из цикла состояния)
       AI_ContinueRoutine(self);
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
       // продолжить распорядок дня НПС
       AI_ContinueRoutine(self);
   };
};

// Функция реакции на локальное восприятие магии (заклинания)
func void B_RestartBurn()
{
   // если последнее примененное заклинание
   if (Npc_GetLastHitSpellID(self) == SPL_Firerain)            // Огненный дождь
   || (Npc_GetLastHitSpellID(self) == SPL_ChargeFireball)      // или Большой огненный шар
   || (Npc_GetLastHitSpellID(self) == SPL_InstantFireball)     // или Огненный шар
   || (Npc_GetLastHitSpellID(self) == SPL_Firebolt)            // или Огненная стрела
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
       return;
   };
   // если последнее примененное заклинание
   if (Npc_GetLastHitSpellID(self) == SPL_IceWave)             // Ледяная волна
   || (Npc_GetLastHitSpellID(self) == SPL_IceCube)             // или Ледяная глыба
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // очистить восприятия
       B_ClearPerceptions(self);
       // перевести жертву в состояние Магической заморозки
       AI_StartState(self,ZS_MagicFreeze,0,"");
   };
};

// Инициализация состояния
func void ZS_MagicBurn()
{
   // установить восприятие прекращения действия магии
   Npc_PercEnable(self,PERC_ASSESSSTOPMAGIC,B_StopMagicBurn);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
};

// Функция цикла состояния
func int ZS_MagicBurn_Loop()
{
   // разрешить локальное восприятие на воздействие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartBurn);
   // если прошла 1 секунда
   if(Npc_GetStateTime(self) == 1)
   {
       // сбросить время состояния
       Npc_SetStateTime(self,0);
       // нанести жертве секундный урон
       B_MagicHurtNpc(other,self,SPL_MAGICBURN_DAMAGE_PER_SEC);
   };
   // если жизнь жертвы кончилась
   if(self.attribute[ATR_HITPOINTS] <= 0)
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       return LOOP_END;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicBurn_End()
{
};

// ****************************************************
// Обработчик состояния Короткого магического горения
// ****************************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

const int SPL_MAGICBURNSHORT_DAMAGE_PER_SEC = 1;    // ущерб в сек от заклинания

// Закончить действие заклинания
func void B_StopMagicBurnShort()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
       // продолжить распорядок дня НПС (т.к. завершение этого состояния выполняется не из цикла состояния)
       AI_ContinueRoutine(self);
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
       // продолжить распорядок дня НПС
       AI_ContinueRoutine(self);
   };
};

// Функция реакции на локальное восприятие магии (заклинания)
func void B_RestartBurnShort()
{
   // если последнее примененное заклинание
   if (Npc_GetLastHitSpellID(self) == SPL_ChargeFireball)  // Большой огненный шар
   || (Npc_GetLastHitSpellID(self) == SPL_Firestorm)       // или Малая огненная буря
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
       return;
   };
   // если последнее примененное заклинание
   if (Npc_GetLastHitSpellID(self) == SPL_IceWave)         // Ледяная волна
   || (Npc_GetLastHitSpellID(self) == SPL_IceCube)         // или Ледяная глыба
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // очистить восприятия
       B_ClearPerceptions(self);
       // перевести жертву в состояние Магической заморозки
       AI_StartState(self,ZS_MagicFreeze,0,"");
   };
};

// Инициализация состояния
func void ZS_MagicBurnShort()
{
   // установить восприятие прекращения действия магии
   Npc_PercEnable(self,PERC_ASSESSSTOPMAGIC,B_StopMagicBurnShort);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp (self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick (self);
   };
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // анимация горения
       Npc_PlayAni(self,"S_FIRE_VICTIM");
   };
};

// Функция цикла состояния
func int ZS_MagicBurnShort_Loop()
{
   // разрешить локальное восприятие на воздействие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartBurnShort);
   // если прошла 1 секунда
   if(Npc_GetStateTime(self) == 1)
   {
       // сбросить время состояния
       Npc_SetStateTime(self,0);
       // нанести жертве секундный урон
       B_MagicHurtNpc(other,self,SPL_MAGICBURNSHORT_DAMAGE_PER_SEC);
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       return LOOP_END;
   };
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // анимация горения
       Npc_PlayAni(self,"S_FIRE_VICTIM");
   };
   // если жизнь жертвы кончилась
   if(self.attribute[ATR_HITPOINTS] <= 0)
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает
       AI_StandUp(self);
       return LOOP_END;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicBurnShort_End()
{
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
};

// **************************************
// Обработчик состояния страха
// **************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Закончить действие заклинания
func void B_StopMagicFlee()
{
   // запретить восприятие повреждений
   Npc_PercDisable(self,PERC_ASSESSDAMAGE);
   // восстановить other на агрессора
   Npc_SetTarget(self,other);
   // переход в состояние убегания от агрессора
   AI_StartState(self,ZS_Flee,0,"");
};

// Инициализация состояния
func void ZS_MagicFlee ()
{
   // если жертва Дракон
   if(self.guild == GIL_DRAGON)
   {
       // продолжить распорядок дня НПС (нет реакции на магию)
       AI_ContinueRoutine(self);
   };
   var int randy;
   // установить восприятие прекращения действия магии
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_StopMagicFlee);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // сбросить статус предупреждений охраны
   self.aivar[AIV_Guardpassage_Status] = GP_NONE;
   // сбросить счетчик отказа от диалога
   Npc_SetRefuseTalk(self,0);
   // установить временное отношение между жертвой и ГГ равное постоянному
   Npc_SetTempAttitude(self,Npc_GetPermAttitude(self,hero));
   // НПС и ГГ перестают смотреть друг на друга
   B_StopLookAt(self);
   // НПС прекращает на что-либо указывать
   AI_StopPointAt(self);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       randy = Hlp_Random (3);
       // проигрывается анимация
       if(randy == 0)  { AI_PlayAniBS(self,"T_STAND_2_FEAR_VICTIM1",BS_STAND); };
       if(randy == 1)  { AI_PlayAniBS(self,"T_STAND_2_FEAR_VICTIM2",BS_STAND); };
       if(randy == 2)  { AI_PlayAniBS(self,"T_STAND_2_FEAR_VICTIM3",BS_STAND); };
   };
};

// Функция цикла состояния
func int ZS_MagicFlee_Loop()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_Time_Fear)
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // закончить действие обработчика
       B_StopMagicFlee();
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicFlee_End()
{
};

// *******************************************
// Обработчик состояния Магической заморозки
// *******************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Функция реакции на локальное восприятие магии (заклинания)
func void B_RestartFreeze()
{
   // если последнее примененное заклинание
   if (Npc_GetLastHitSpellID(self) == SPL_IceCube)     // Ледяная глыба
   || (Npc_GetLastHitSpellID(self) == SPL_IceWave)     // или Ледяная волна
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
   };
};

// Закончить действие заклинания
func void B_StopMagicFreeze()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
   };
};

// Инициализация состояния
func void ZS_MagicFreeze()
{
   // разрешить локальное восприятие на воздействие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartFreeze);
   // прекратить анимацию
   Npc_StopAni(self,"S_FIRE_VICTIM");
   // если состояние тела НПС не прерываемо
   if(!C_BodyStateContains(self,BS_UNCONSCIOUS))
   {
       // анимация заморозки
       AI_PlayAniBS(self,"T_STAND_2_FREEZE_VICTIM",BS_UNCONSCIOUS);
   };
   // сброс времени заморозки
   self.aivar[AIV_FreezeStateTime] = 0;
};

// Функция цикла состояния
func int ZS_MagicFreeze_Loop()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_TIME_FREEZE)
   {
       // закончить действие обработчика
       B_StopMagicFreeze();
       return LOOP_END;
   };
   // если время в состоянии не равно времени заморозки
   if(Npc_GetStateTime(self) != self.aivar[AIV_FreezeStateTime])
   {
       // время заморозки = времени состояния
       self.aivar[AIV_FreezeStateTime] = Npc_GetStateTime(self);
       // если жизнь жертвы > ущерба от заклинания
       if(self.attribute[ATR_HITPOINTS] > (self.attribute[ATR_HITPOINTS] - SPL_FREEZE_DAMAGE))
       {
           // если жертва Огненный голем или
           if (self.guild == GIL_FIREGOLEM)
           || (self.aivar[AIV_MM_REAL_ID]  ==  ID_FIREWARAN)       // Огненная ящерица
           || (self.aivar[AIV_MM_REAL_ID]  ==  ID_DRAGON_FIRE)     // или Огненный дракон
           {
               // нанести удвоенное повреждение в сек
               B_MagicHurtNpc(other,self,SPL_FREEZE_DAMAGE*2);
               return LOOP_CONTINUE;
           };
           // если жертва Ледяной голем или Ледяной дракон
           if(self.guild == GIL_ICEGOLEM) || (self.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)
           {
               // нанести половинное порреждение в сек
               B_MagicHurtNpc(other,self,SPL_FREEZE_DAMAGE/2);
               return LOOP_CONTINUE;
           };
           // нанести обычное повреждение в сек
           B_MagicHurtNpc(other,self,SPL_FREEZE_DAMAGE);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicFreeze_End()
{
};

// ***************************************
// Обработчик состояния Уменьшить монстра
// ***************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Инициализация состояния
func void ZS_MagicShrink()
{
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
   // сброс времени уменьшения
   self.aivar[AIV_MM_ShrinkState] = 0;
};

// Функция цикла состояния
func int ZS_MagicShrink_Loop()
{
   // за 13 секунд плавно уменьшить жертву
   if     (self.aivar[AIV_MM_ShrinkState] == 0)    { Mdl_SetModelScale(self,0.90,0.90,0.90); self.aivar[AIV_MM_ShrinkState] = 1; }
   else if(self.aivar[AIV_MM_ShrinkState] == 1)    { Mdl_SetModelScale(self,0.85,0.85,0.85); self.aivar[AIV_MM_ShrinkState] = 2; }
   else if(self.aivar[AIV_MM_ShrinkState] == 2)    { Mdl_SetModelScale(self,0.80,0.80,0.80); self.aivar[AIV_MM_ShrinkState] = 3; }
   else if(self.aivar[AIV_MM_ShrinkState] == 3)    { Mdl_SetModelScale(self,0.75,0.75,0.75); self.aivar[AIV_MM_ShrinkState] = 4; }
   else if(self.aivar[AIV_MM_ShrinkState] == 4)    { Mdl_SetModelScale(self,0.70,0.70,0.70); self.aivar[AIV_MM_ShrinkState] = 5; }
   else if(self.aivar[AIV_MM_ShrinkState] == 5)    { Mdl_SetModelScale(self,0.65,0.65,0.65); self.aivar[AIV_MM_ShrinkState] = 6; }
   else if(self.aivar[AIV_MM_ShrinkState] == 6)    { Mdl_SetModelScale(self,0.60,0.60,0.60); self.aivar[AIV_MM_ShrinkState] = 7; }
   else if(self.aivar[AIV_MM_ShrinkState] == 7)    { Mdl_SetModelScale(self,0.55,0.55,0.55); self.aivar[AIV_MM_ShrinkState] = 8; }
   else if(self.aivar[AIV_MM_ShrinkState] == 8)    { Mdl_SetModelScale(self,0.50,0.50,0.50); self.aivar[AIV_MM_ShrinkState] = 9; }
   else if(self.aivar[AIV_MM_ShrinkState] == 9)    { Mdl_SetModelScale(self,0.45,0.45,0.45); self.aivar[AIV_MM_ShrinkState] = 10;}
   else if(self.aivar[AIV_MM_ShrinkState] == 10)   { Mdl_SetModelScale(self,0.40,0.40,0.40); self.aivar[AIV_MM_ShrinkState] = 11;}
   else if(self.aivar[AIV_MM_ShrinkState] == 11)   { Mdl_SetModelScale(self,0.35,0.35,0.35); self.aivar[AIV_MM_ShrinkState] = 12;}
   else if(self.aivar[AIV_MM_ShrinkState] == 12)   { Mdl_SetModelScale(self,0.30,0.30,0.30); self.aivar[AIV_MM_ShrinkState] = 13;}
   // если жертва уменьшена
   else if(self.aivar[AIV_MM_ShrinkState] == 13)
   {
       // уменьшить жизнь жертвы
       Npc_ChangeAttribute(self,ATR_HITPOINTS,-((self.attribute[ATR_HITPOINTS]*9)/10));
       // уменьшить макс. жизнь жертвы
       Npc_ChangeAttribute(self,ATR_HITPOINTS_MAX,-((self.attribute[ATR_HITPOINTS_MAX]*9)/10));
       // уменьшить силу жертвы
       Npc_ChangeAttribute(self,ATR_STRENGTH,-((self.attribute[ATR_STRENGTH]*9)/10));
       // уменьшить защиту от тупого оружия
       self.protection[PROT_BLUNT] = self.protection[PROT_BLUNT]/10;
       // уменьшить защиту от острого оружия
       self.protection[PROT_EDGE] = self.protection[PROT_EDGE]/10;
       // уменьшить защиту от огня
       self.protection[PROT_FIRE] = self.protection[PROT_FIRE]/10;
       // защита от нокаута = 0
       self.protection[PROT_FLY] = 0;
       // уменьшить защиту от магии
       self.protection[PROT_MAGIC] = self.protection[PROT_MAGIC]/10;
       // уменьшить защиту от тотечного оружия
       self.protection[PROT_POINT] = self.protection[PROT_POINT]/10;
       // переход на следующую фазу состояния
       self.aivar[AIV_MM_ShrinkState] = 14;
   }
   // если достигнута последняя фаза состояния
   else if(self.aivar[AIV_MM_ShrinkState] == 14)
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // установить обработчик поведения в уменьшенном состоянии
       self.start_aistate = ZS_MagicShrunk;
       // переход в новый обработчик
       AI_StartState(self,ZS_MagicShrunk,1,"");
       return LOOP_END;
   };
   // задержка перед последней фазой состояния
   AI_Wait(self,0.38 );
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicShrink_End()
{
};

// ********************************************
// Обработчик поведения уменьшенного монстра
// ********************************************

// Инициализация состояния
func void ZS_MagicShrunk()
{
   // установить целью ГГ
   Npc_SetTarget(self,hero);
};

// Функция цикла состояния
func int ZS_MagicShrunk_Loop()
{
   // монстр убегает от ГГ
   AI_Flee(self);
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicShrunk_End()
{
};

// **************************************
// Обработчик состояния Магический сон
// **************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Локальное восприятие повреждения (окончание действия заклинания)
func int B_StopMagicSleep()
{
   // запретить восприятие повреждений
   Npc_PercDisable(self,PERC_ASSESSDAMAGE);
   // жертва говорит агрессору (SVM фраза) "(сам с собою, просыпаясь) Что это было!?" (Примечание: Ошибка слово $WHATWASSTHAT надо заменить на $WHATWASTHAT, а то фраза не будет сказана)
   B_Say_Overlay(self,other,"$WHATWASSTHAT");
   // анимация перехода в нормальное состояние
   AI_PlayAni(self,"T_VICTIM_SLE_2_STAND");
};

// Локальное восприятие разговора
func void B_AssessMagicSleepTalk()
{
   // жертва говорит агрессору (SVM фраза) "(просыпаясь) Черт, что случилось?"
   B_Say(self,other,"$YOUDISTURBEDMYSLUMBER");
   // жертва переходит в состояние наблюдения за ГГ
   AI_StartState(self,ZS_ObservePlayer,1,"");
};

// Инициализация состояния
func void ZS_MagicSleep()
{
   // установить локальное восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_StopMagicSleep);
   // разрешить восприятие магии
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // установить локальное восприятие разговора
   Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessMagicSleepTalk);
   // Сбросить статус предупреждений охраной
   self.aivar[AIV_Guardpassage_Status] = GP_NONE;
   // сбросить счетчик отказа от диалога
   Npc_SetRefuseTalk(self,0);
   // установить временное отношение между жертвой и ГГ равное постоянному
   Npc_SetTempAttitude(self,Npc_GetPermAttitude(self,hero));
   // НПС и ГГ перестают смотреть друг на друга
   B_StopLookAt(self);
   // НПС прекращает на что-либо указывать
   AI_StopPointAt(self);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
   // проигрывается анимация перехода в Магический сон
   AI_PlayAniBS(self,"T_STAND_2_VICTIM_SLE",BS_LIE );
};

// Функция цикла состояния
func int ZS_MagicSleep_Loop()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_Time_Sleep)
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // закончить действие обработчика
       B_StopMagicSleep();
       return LOOP_END;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_MagicSleep_End()
{
};

// ***********************************************************
// Обработчик состояния Большой огненной бури
// -----------------------------------------------------------
// В это состояние НПС переводится экзешником, а не скриптами
// ***********************************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Закончить действие заклинания
func void B_StopPyro()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
       // продолжить распорядок дня НПС (т.к. не было принудительного перевода НПС в это состояние из скриптов и завершение этого состояния выполняется не из цикла состояния)
       AI_ContinueRoutine(self);
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
       // продолжить распорядок дня НПС
       AI_ContinueRoutine(self);
   };
};

// Инициализация состояния
func void ZS_Pyro()
{
   // установить восприятие прекращения действия магии
   Npc_PercEnable(self,PERC_ASSESSSTOPMAGIC,B_StopPyro);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self, BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // проиграть анимацию входа в состояние
       AI_PlayAni(self,"T_STAND_2_LIGHTNING_VICTIM");
   };
};

// Функция цикла состояния
func int ZS_Pyro_Loop()
{
   // если прошла 1 секунда
   if(Npc_GetStateTime(self) >= 1)
   {
       // сбросить время состояния
       Npc_SetStateTime(self,0);
       // нанести жертве секундный урон
       B_MagicHurtNpc(other,self,SPL_PYRO_DAMAGE_PER_SEC);
       // если жизнь жертвы кончилась
       if(self.attribute[ATR_HITPOINTS] <= 0)
       {
           // очистить очередь AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает
           AI_StandUp(self);
           return LOOP_END;
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Pyro_End()
{
};

// ************************************************
// Обработчик состояния реакции на Шаровую молнию
// ************************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Закончить действие заклинания
func void B_StopShortZapped()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
       // продолжить распорядок дня НПС (т.к. завершение этого состояния выполняется не из цикла состояния)
       AI_ContinueRoutine(self);
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
       // продолжить распорядок дня НПС
       AI_ContinueRoutine(self);
   };
};

// Инициализация состояния
func void ZS_ShortZapped()
{
   // установить восприятие прекращения действия магии
   Npc_PercEnable(self,PERC_ASSESSSTOPMAGIC,B_StopShortZapped);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // проиграть анимацию входа в состояние
       AI_PlayAni(self,"T_STAND_2_LIGHTNING_VICTIM");
   };
};

// Функция цикла состояния
func int ZS_ShortZapped_Loop()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_TIME_SHORTZAPPED)
   {
       // закончить действие обработчика
       B_StopShortZapped();
       return LOOP_END;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_ShortZapped_End()
{
};

// *************************************
// Обработчик состояния Украсть энергию
// *************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

var int Temp_SuckEnergy_DistToPlayer;   // расстояние между жертвой и ГГ

// Функция реакции на локальное восприятие магии (заклинания)
func void B_RestartSuckEnergy()
{
   // если последнее примененное заклинание Украсть энергию
   if(Npc_GetLastHitSpellID(self) == SPL_SuckEnergy)
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
       // анимация состояния
       AI_PlayAni(self,"T_STAND_2_SUCKENERGY_VICTIM");
   };
};

// Закончить действие заклинания
func void B_StopSuckEnergy()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
   };
};

// Инициализация состояния
func void ZS_SuckEnergy()
{
   // установить локальное восприятие на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartSuckEnergy);
   // остановить проигрывание файла анимации заклинания
   Npc_StopAni(self, "S_SUCKENERGY_VICTIM");
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // сброс времени цикла
   self.aivar[AIV_SuckEnergyStateTime] = 1;
   // запомнить расстояние между жертвой и ГГ
   Temp_SuckEnergy_DistToPlayer = Npc_GetDistToPlayer(self);
};

// Функция цикла состояния
func int ZS_SuckEnergy_Loop()
{
   // если время в состоянии > времени действия заклинания или расстояние между ГГ и жертвой увеличилось на более 10м
   if(Npc_GetStateTime(self) > SPL_TIME_SuckEnergy) || (Temp_SuckEnergy_DistToPlayer >= Npc_GetDistToPlayer(self) + 100)
   {
       // остановить действие заклинания
       B_StopSuckEnergy();
       return LOOP_END;
   };
   // если текущее время состояния не равно времени цикла
   if(Npc_GetStateTime(self) != self.aivar[AIV_SuckEnergyStateTime])
   {
       // если первая фаза состояния
       if(Npc_GetStateTime(self) == 0)
       {
           // если состояние тела НПС не прерываемо
           if(!C_BodyStateContains(self, BS_UNCONSCIOUS))
           {
               // анимация перехода в бессознательное состояние
               AI_PlayAniBS(self,"T_STAND_2_SUCKENERGY_VICTIM",BS_UNCONSCIOUS);
           };
           // эффект высасывания энергии
           Wld_PlayEffect("spellFX_SuckEnergy_BloodFly",self,hero,0,0,0,FALSE);
       };
       // обновить время цикла
       self.aivar[AIV_SuckEnergyStateTime] = Npc_GetStateTime(self);
       // если жизнь жертвы > ущерба от заклинания
       if(self.attribute[ATR_HITPOINTS] > SPL_SuckEnergy_DAMAGE)
       {
           // нанести жертве урон
           B_MagicHurtNpc(other,self,SPL_SuckEnergy_DAMAGE);
           // добавить украденную жизнь агрессору
           Npc_ChangeAttribute(other,ATR_HITPOINTS,SPL_SuckEnergy_DAMAGE);
       }
       else
       {
           // убить жертву
           B_MagicHurtNpc(other,self,self.attribute[ATR_HITPOINTS] - 1);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_SuckEnergy_End()
{
};

// *************************************
// Обработчик состояния заклинания Рой
// *************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Функция реакции на локальное восприятие магии
func void B_RestartSwarm()
{
   // если последнее примененное заклинание Рой
   if(Npc_GetLastHitSpellID(self) == SPL_Swarm)
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
   };
};

// Закончить действие заклинания
func void B_StopSwarm()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if (self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
   };
};

// Инициализация состояния
func void ZS_Swarm()
{
   // установить локальное восприятие на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartSwarm);
   // остановить проигрывание файла анимации заклинания
   Npc_StopAni(self,"S_SWARM_VICTIM");
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если состояние тела НПС не прерываемо
   if(!C_BodyStateContains(self,BS_UNCONSCIOUS))
   {
       // анимация перехода в бессознательное состояние
       AI_PlayAniBS(self,"T_STAND_2_SWARM_VICTIM",BS_UNCONSCIOUS);
   };
   // сброс времени цикла
   self.aivar[AIV_SwarmStateTime] = 0;
};

// Функция цикла состояния
func int ZS_Swarm_Loop ()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_TIME_Swarm)
   {
       // остановить действие заклинания
       B_StopSwarm();
       return LOOP_END;
   };
   // если прошла 1 сек
   if(Npc_GetStateTime(self) != self.aivar[AIV_SwarmStateTime])
   {
       // если жертва находится в состоянии 2 сек
       if(Npc_GetStateTime(self) == 2)
       {
           // жертва говорит агрессору (SVM фраза) "Ах, черт!"
           B_Say(self,other,"$RunAway");
       }
       // через 6 сек
       else if(Npc_GetStateTime(self) == 6)
       {
           // жертва говорит агрессору (SVM фраза) "Ууух!"
           B_Say(self,other,"$Aargh_2");
       }
       // через 7 сек
       else if(Npc_GetStateTime(self) == 7)
       {
           // жертва говорит агрессору (SVM фраза) "Ууух!"
           B_Say (self,other,"$Aargh_3");
       }
       // через 8 сек
       else if(Npc_GetStateTime(self) == 8 )
       {
           // жертва говорит агрессору (SVM фраза) "Ууух!"
           B_Say(self,other,"$Aargh_1");
       };
       // установить счетчик цикла на текущее время
       self.aivar[AIV_SwarmStateTime] = Npc_GetStateTime(self);
       // если жизнь жертвы > ущерба от заклинания
       if(self.attribute[ATR_HITPOINTS] > SPL_Swarm_DAMAGE)
       {
           // нанести жертве урон
           B_MagicHurtNpc(other,self,SPL_Swarm_DAMAGE);
       }
       else
       {
           // убить жертву
           B_MagicHurtNpc(other,self,self.attribute[ATR_HITPOINTS] - 1);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Swarm_End()
{
};

// ******************************************
// Обработчик состояния заклинания Смерч
// ******************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Функция реакции на локальное восприятие магии
func void B_RestartWhirlwind()
{
   // если последнее примененное заклинание Смерч
   if(Npc_GetLastHitSpellID(self) == SPL_Whirlwind)
   {
       // сбросить время нахождения НПС в этом состоянии
       Npc_SetStateTime(self,0);
   };
};

// Закончить действие заклинания
func void B_StopWhirlwind()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
   };
};

// Инициализация состояния
func void ZS_Whirlwind()
{
   // установить локальное восприятие на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_RestartWhirlwind);
   // остановить проигрывание файла анимации заклинания
   Npc_StopAni(self,"S_WHIRLWIND_VICTIM");
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если состояние тела НПС не прерываемо
   if(!C_BodyStateContains(self, BS_UNCONSCIOUS))
   {
       // анимация перехода в бессознательное состояние
       AI_PlayAniBS(self,"T_STAND_2_WHIRLWIND_VICTIM",BS_UNCONSCIOUS);
   };
   // сброс счетчика цикла
   self.aivar[AIV_WhirlwindStateTime] = 0;
};

// Функция цикла состояния
func int ZS_Whirlwind_Loop ()
{
   // если время в состоянии > времени действия заклинания
   if(Npc_GetStateTime(self) > SPL_TIME_WHIRLWIND)
   {
       // остановить действие заклинания
       B_StopWhirlwind();
       return LOOP_END;
   };
   // если прошла 1 сек
   if(Npc_GetStateTime(self) != self.aivar[AIV_WhirlwindStateTime])
   {
       // установить счетчик цикла на текущее время
       self.aivar[AIV_WhirlwindStateTime] = Npc_GetStateTime(self);
       // если жизнь жертвы > ущерба от заклинания
       if(self.attribute[ATR_HITPOINTS] > SPL_Whirlwind_DAMAGE)
       {
           // нанести жертве урон
           B_MagicHurtNpc(other,self,SPL_Whirlwind_DAMAGE);
       }
       else
       {
           // убить жертву
           B_MagicHurtNpc(other,self,self.attribute[ATR_HITPOINTS] - 1);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Whirlwind_End()
{
};

// ***********************************************************
// Обработчик состояния Удар молнии
// -----------------------------------------------------------
// В это состояние НПС переводится экзешником, а не скриптами
// ***********************************************************

// ----------------------------------------------------
// self - жертва заклинания
// other - кастующий НПС (агрессор)
// ----------------------------------------------------

// Закончить действие заклинания
func void B_StopZapped()
{
   // возвратить общую функцию состояния реакции на магию
   Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic);
   // очистить очередь AI состояний НПС
   Npc_ClearAIQueue(self);
   // НПС встает
   AI_StandUp(self);
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // реакция НПС на повреждение
       B_AssessDamage();
       // продолжить распорядок дня НПС (т.к. не было принудительного перевода НПС в это состояние из скриптов и завершение этого состояния выполняется не из цикла состояния)
       AI_ContinueRoutine(self);
   }
   else
   {
       // установить временно враждебное отношение НПС к ГГ
       Npc_SetTempAttitude(self,ATT_HOSTILE);
       // продолжить распорядок дня НПС
       AI_ContinueRoutine(self);
   };
};

// Инициализация состояния
func void ZS_Zapped()
{
   // установить восприятие прекращения действия магии
   Npc_PercEnable(self,PERC_ASSESSSTOPMAGIC,B_StopZapped);
   // если состояние тела НПС не прерываемо
   if(!Npc_HasBodyFlag(self,BS_FLAG_INTERRUPTABLE))
   {
       // НПС встает
       AI_StandUp(self);
   }
   else
   {
       // НПС быстро встает
       AI_StandUpQuick(self);
   };
   // если жертва человек
   if(self.guild < GIL_SEPERATOR_HUM)
   {
       // включить анимацию действия заклинания
       AI_PlayAni(self,"T_STAND_2_LIGHTNING_VICTIM");
   };
};

// Функция цикла состояния
func int ZS_Zapped_Loop()
{
   // если прошла 1 секунда
   if(Npc_GetStateTime(self) >= 1)
   {
       // сбросить время состояния
       Npc_SetStateTime(self,0);
       // нанести жертве секундный урон
       B_MagicHurtNpc(other,self,SPL_ZAPPED_DAMAGE_PER_SEC);
       // если жизнь жертвы кончилась
       if(self.attribute[ATR_HITPOINTS] <= 0)
       {
           // очистить очередь AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает
           AI_StandUp(self);
           return LOOP_END;
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Zapped_End()
{
};


18. Разные AI функции.

// ***********************************************
// Функция обновления снаряжения при респавне НПС
// -----------------------------------------------
// Вызывается только экзешником
// ***********************************************

// -----------------------------------------------
// self - возрождаемый НПС
// -----------------------------------------------

func void B_RefreshAtInsert()
{
   // если НПС мертв
   if(self.attribute[ATR_HITPOINTS] <= 0)
   {
       return;
   };
   var c_npc her;
   // получить ссылку на ГГ
   her = Hlp_GetNpc(PC_Hero);
   // если НПС человек и НПС не ГГ
   if(self.guild < GIL_SEPERATOR_HUM) && (Hlp_GetInstanceID(self) != Hlp_GetInstanceID(her))
   {
       // установить текущую жизнь равной максимальной
       self.attribute[ATR_HITPOINTS] = self.attribute[ATR_HITPOINTS_MAX];
       // если НПС не имеет никакого оружия
       if(Npc_HasEquippedWeapon(self) == FALSE)
       {
           // если НПС милиционер
           if(self.guild == GIL_MIL)
           {
               // создать в инвентаре Тяжелую палку
               CreateInvItems(self,ItMw_1h_Bau_Mace,1);
           };
           // если НПС паладин
           if(self.guild == GIL_PAL)
           {
               CreateInvItems(self,ItMw_1h_Bau_Mace,1);
           };
           // если НПС наемник
           if(self.guild == GIL_SLD)
           {
               CreateInvItems(self,ItMw_1h_Bau_Mace,1);
           };
           // если НПС крестьянин
           if(self.guild == GIL_BAU)
           {
               CreateInvItems(self,ItMw_1h_Bau_Mace,1);
           };
           // если НПС маг послушник
           if(self.guild == GIL_NOV)
           {
               CreateInvItems(self,ItMw_1h_Bau_Mace,1);
           };
       };
   };
};
Эта функция расположена в директории ..\AI\AI_Intern в одноименном файле.

// ***********************************************************
// Функция реплик НПС при обыскивании тел, когда нечего взять
// -----------------------------------------------------------
// Вызывается только экзешником
// ***********************************************************

// -----------------------------------------------
// self - обыскивающий НПС
// -----------------------------------------------

func void PLAYER_PLUNDER_IS_EMPTY()
{
   var int rnd;
   rnd = Hlp_Random(100);
   // с вероятностью 0.4
   if(rnd <= 40)
   {
       // напечатать текст по цетру экрана "здесь ничего нет..."
       Print(PRINT_NOTHINGTOGET);
       // произносится SVM фраза "Здесь нечего унести ..."
       B_Say_Overlay(self,self,"$NOTHINGTOGET");
   }
   // с вероятностью 0.4
   else if(rnd <= 80)
   {
       // напечатать текст по цетру экрана "Нечего унести..."
       Print(PRINT_NOTHINGTOGET02);
       // произносится SVM фраза "нечего унести ..."
       B_Say_Overlay(self,self,"$NOTHINGTOGET02");
   }
   // с вероятностью 0.2
   else if(rnd <= 99)
   {
       // напечатать текст по цетру экрана "Нечего грабить..."
       Print(PRINT_NOTHINGTOGET03);
       // произносится SVM фраза "нечего украсть ..."
       B_Say_Overlay(self,self,"$NOTHINGTOGET03");
   };
};
Эта функция расположена в директории ..\AI\AI_Intern в одноименном файле.

//**************************************************************
//*****
// Функция обновления доспехов НПС
// -------------------------------------------------------------------
// Вызывается только экзешником (пока пуста)
// *******************************************************************

func void B_RefreshArmor()
{
};
Эта функция расположена в директории ..\AI\Human в одноименном файле.

B_ функции монстров расположены в директории ..\AI\Monster\B_Monster
// ***********************************
// Функция задержки реакции монстров
// ***********************************

func void B_MM_DeSynchronize()
{
   var int msec;
   msec = Hlp_Random(1000);
   // случайная задержка от 0 до 1 сек
   AI_Waitms(self,msec);
};

// *******************************************
// Выполнение Огоньком заданий
// *******************************************

// ----------------------------------------------------
// self - Огонек
// item - предмет
// ----------------------------------------------------

// Реакция Огонька на найденный предмет
func void B_GetWispDetectedItem()
{
   // если предмет не существует
   if(!Hlp_IsValidItem(item))
   {
       return;
   };
   // если расстояние между Огоньком и предметом > 5м
   if(Npc_GetHeightToItem(self,item) > 500)
   {
       return;
   };
   var int randy;
   randy = Hlp_Random(100);
   // Огонек движется к предмету
   AI_GotoItem(self,item);
   // если расстояние между Огоньком и предметом < 1м и вероятность <= 0.01
   if(Npc_GetDistToItem(self,item) < 1000) && (randy <= 1)
   {
       // проигрывается анимация выкапывания предмета
       Wld_PlayEffect("spellFX_ItemAusbuddeln",item,item,0,0,0,FALSE );
   };
};

// Поиск Огоньком предметов
//----------------------------------------------------------------
// Возвращаемое значение: что нашел Огонек (в принципе не нужно)
func int B_WispDetectedItem()
{
   // если Огонек ничего не ищет
   if(WispSearching == WispSearch_Follow)
   {
   }
   else // иначе (режим поиска)
   {
       var int WispSearchFlags;
       // флаг поиска
       WispSearchFlags = 0;
       // если Огонек обучен искать оружие ближнего радиуса поражения и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_NF] == TRUE)
       // (режим поиска - искать всё или оружие ближнего радиуса поражения)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_NF)))
       {
           // установить соответствующий флаг
           WispSearchFlags = WispSearchFlags | ITEM_KAT_NF;
       };
       // если Огонек обучен искать оружие дальнего радиуса поражения и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_FF] == TRUE)
       // (режим поиска - искать всё или оружие дальнего радиуса поражения)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_FF)))
       {
           WispSearchFlags = WispSearchFlags | ITEM_KAT_FF | ITEM_KAT_MUN;
       };
       // если Огонек обучен искать деньги, ключи, предметы обихода и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_NONE] == TRUE)
       // (режим поиска - искать всё или указанные вещи)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_NONE)))
       {
           WispSearchFlags = WispSearchFlags | ITEM_KAT_NONE | ITEM_KAT_KEYS | ITEM_KAT_LIGHT | ITEM_KAT_ARMOR;
       };
       // если Огонек обучен искать руны и свитки и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_RUNE] == TRUE)
       // (режим поиска - искать всё или указанные вещи)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_RUNE)))
       {
           WispSearchFlags = WispSearchFlags | ITEM_KAT_RUNE | ITEM_KAT_DOCS;
       };
       // если Огонек обучен искать кольца и амулеты и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_MAGIC] == TRUE)
       // (режим поиска - искать всё или указанные вещи)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_MAGIC)))
       {
           WispSearchFlags = WispSearchFlags | ITEM_KAT_MAGIC;
       };
       // если Огонек обучен искать пропитание и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_FOOD] == TRUE)
       // (режим поиска - искать всё или указанные вещи)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_FOOD)))
       {
           WispSearchFlags = WispSearchFlags | ITEM_KAT_FOOD;
       };
       // если Огонек обучен искать напитки и
       if ((PLAYER_TALENT_WISPDETECTOR[WISPSKILL_POTIONS] == TRUE)
       // (режим поиска - искать всё или указанные вещи)
       && ((WispSearching == WispSearch_ALL) || (WispSearching == WispSearch_POTIONS)))
       {
           WispSearchFlags = WispSearchFlags | ITEM_KAT_POTIONS;
       };
       // если Огонек способен что-то найти
       if(WispSearchFlags != 0)
       {
           // если найден предмет с указанными флагами
           if(Wld_DetectItem(self,WispSearchFlags))
           {
               // реакция Огонька на найденный предмет
               B_GetWispDetectedItem();
           };
       };
       return WispSearchFlags;
   };
   return 0;
};

// Выполнение Огоньком заданий
func int B_MM_WispDetect()
{
   // если self (вызванный монстр) есть Огонек
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Wisp_Detector))
   {
       // разрешить Огоньку воспринимать все объекты в зоне действия восприятия
       Npc_PerceiveAll(self);
       // если найден другой Огонек (other инициализирован Огоньком)
       if(Wld_DetectNpc(self,Wisp_Detector,NOFUNC,-1)
       {
           // удалить Огонек
           B_RemoveNpc(self);
       };
       // если расстояние между Огоньком и ГГ меньше 20м и установлен флаг близости
       if(Npc_GetDistToNpc(self,hero) < 2000) && (self.aivar[AIV_TAPOSITION] == ISINPOS)
       {
           // сброс времени жизни монстра
           self.aivar[AIV_SummonTime] = 0;
           // выполнение поиска Огоньком
           B_WispDetectedItem();
           return LOOP_END;
       }
       else
       {
           // если расстояние между Огоньком и ГГ меньше 5м
           if(Npc_GetDistToNpc(self,hero) < 500)
           {
               // установить флаг близости
               self.aivar[AIV_TAPOSITION] = ISINPOS;
           }
           else
           {
               // установить флаг удаленности
               self.aivar[AIV_TAPOSITION] = NOTINPOS;
               // очистка очереди AI состояний Огонька
               Npc_ClearAIQueue(self);
               // Огонек движется к ГГ
               AI_GotoNpc(self,hero);
           };
           return LOOP_END;
       };
   };
   return LOOP_CONTINUE;
};
C_ функции монстров расположены в директории ..\AI\Monsrer\C_Monster
// ********************************************************************
// Является ли монстр магом
// ********************************************************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый монстр
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_NpcIsMonsterMage(var c_npc slf)
{
   // если монстр Маг скелетов или
   if (slf.guild == GIL_SKELETON_MAGE)
   || (slf.guild == GIL_FIREGOLEM)     // Огненный голем
   || (slf.guild == GIL_ICEGOLEM)      // или Ледяной голем
   || (slf.guild == GIL_DRAGON)        // или Дракон
   {
       return TRUE;
   };
   return FALSE;
};

// ********************************************************************************
//*****
// Устраивает ли монстра добыча в виде жертвы (на кого охотяться монстры)
// ********************************************************************************
//*****

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый монстр (охотник)
// oth - НПС добыча
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_PredatorFoundPrey(var c_npc slf,var c_npc oth)
{
   // если охотник волк, но не кабан
   if(slf.guild == GIL_WOLF) && (slf.aivar[AIV_MM_REAL_ID] != ID_Keiler)
   {
       // если добыча овца
       if(oth.guild == GIL_SHEEP)      { return TRUE; };
       // если добыча крыса
       if(oth.guild == GIL_GIANT_RAT)  { return TRUE; };
       // если добыча кротокрыс
       if(oth.guild == GIL_MOLERAT)    { return TRUE; };
       // если добыча падальщик, но не зубастик
       if(oth.guild == GIL_SCAVENGER) && (oth.aivar[AIV_MM_REAL_ID] != ID_ORCBITER)    { return TRUE; };
   };
   // если охотник шныг
   if(slf.guild == GIL_LURKER)
   {
       // если добыча гоблин
       if (oth.guild == GIL_GOBBO)     { return TRUE; };
   };
   // если охотник глорх
   if(slf.guild == GIL_SNAPPER)
   {
       // если добыча человек
       if(oth.guild < GIL_SEPERATOR_HUM)   { return TRUE; };
       // если добыча гоблин
       if(oth.guild == GIL_GOBBO)          { return TRUE; };
       // если добыча овца
       if(oth.guild == GIL_SHEEP)          { return TRUE; };
       // если добыча крыса
       if(oth.guild == GIL_GIANT_RAT)      { return TRUE; };
       // если добыча кротокрыс
       if(oth.guild == GIL_MOLERAT)        { return TRUE; };
       // если добыча падальщик, но не зубастик
       if(oth.guild == GIL_SCAVENGER) && (oth.aivar[AIV_MM_REAL_ID] != ID_ORCBITER)    { return TRUE; };
   };
   // если охотник мракорис
   if(slf.guild == GIL_SHADOWBEAST)
   {
       // если добыча человек
       if(oth.guild < GIL_SEPERATOR_HUM)   { return TRUE; };
       // если добыча орк
       if(oth.guild > GIL_SEPERATOR_ORC)   { return TRUE; };
       // если добыча овца
       if(oth.guild == GIL_SHEEP)          { return TRUE; };
       // если добыча крыса
       if(oth.guild == GIL_GIANT_RAT)      { return TRUE; };
       // если добыча кротокрыс
       if(oth.guild == GIL_MOLERAT)        { return TRUE; };
       // если добыча падальщик, но не зубастик
       if(oth.guild == GIL_SCAVENGER) && (oth.aivar[AIV_MM_REAL_ID] != ID_ORCBITER)    { return TRUE; };
   };
   return FALSE;
};

// ***********************************
// Пожирает ли монстр труп добычи
// ***********************************

// ====================================================================
// Аргументы:
// --------------------------------------------------------------------
// slf - проверяемый монстр (охотник)
// oth - НПС добыча (труп)
// --------------------------------------------------------------------
// Возвращаемое значение:
// TRUE - да, FALSE - нет.
// ====================================================================

func int C_WantToEat(var c_npc slf,var c_npc oth)
{
   // если монстр член партии ГГ
   if(slf.aivar[AIV_PARTYMEMBER] == TRUE)
   {
       return FALSE;
   };
   // если охотник падальщик
   if(slf.guild == GIL_SCAVENGER)
   {
       // если труп человек или орк
       if(oth.guild < GIL_SEPERATOR_HUM)       {   return TRUE;    };
       if(oth.guild > GIL_SEPERATOR_ORC)       {   return TRUE;    };
       // если труп овца или полевые хищники
       if(oth.guild == GIL_SHEEP)              {   return TRUE;    };
       if(oth.guild == GIL_GIANT_RAT)          {   return TRUE;    };
       if(oth.guild == GIL_MOLERAT)            {   return TRUE;    };
       // если труп гарпия
       if(oth.guild == GIL_HARPY)              {   return TRUE;    };
       // если труп волки
       if(oth.guild == GIL_WOLF)               {   return TRUE;    };
       if(oth.guild == GIL_SUMMONED_WOLF)      {   return TRUE;    };
       // если труп мракорис
       if(oth.guild == GIL_SHADOWBEAST)        {   return TRUE;    };
       // если труп рептилий
       if(oth.guild == GIL_GOBBO)              {   return TRUE;    };
       if(oth.guild == GIL_WARAN)              {   return TRUE;    };
       if(oth.guild == GIL_LURKER)             {   return TRUE;    };
       if(oth.guild == GIL_SNAPPER)            {   return TRUE;    };
       // в принципе здесь все понятно и без комментариев
       if(oth.guild == GIL_MEATBUG)            {   return TRUE;    };
       if(oth.guild == GIL_GIANT_BUG)          {   return TRUE;    };
       if(oth.guild == GIL_BLOODFLY)           {   return TRUE;    };
       if(oth.guild == GIL_MINECRAWLER)        {   return TRUE;    };
   };
   // если охотник крыса
   if(slf.guild == GIL_GIANT_RAT)
   {
       if(oth.guild < GIL_SEPERATOR_HUM)       {   return TRUE;    };
       if(oth.guild > GIL_SEPERATOR_ORC)       {   return TRUE;    };
       if(oth.guild == GIL_SHEEP)              {   return TRUE;    };
       if(oth.guild == GIL_GIANT_RAT)          {   return TRUE;    };
       if(oth.guild == GIL_MOLERAT)            {   return TRUE;    };
       if(oth.guild == GIL_SCAVENGER)          {   return TRUE;    };
       if(oth.guild == GIL_HARPY)              {   return TRUE;    };
       if(oth.guild == GIL_WOLF)               {   return TRUE;    };
       if(oth.guild == GIL_SUMMONED_WOLF)      {   return TRUE;    };
       if(oth.guild == GIL_SHADOWBEAST)        {   return TRUE;    };
       if(oth.guild == GIL_GOBBO)              {   return TRUE;    };
       if(oth.guild == GIL_WARAN)              {   return TRUE;    };
       if(oth.guild == GIL_LURKER)             {   return TRUE;    };
       if(oth.guild == GIL_SNAPPER)            {   return TRUE;    };
       if(oth.guild == GIL_MEATBUG)            {   return TRUE;    };
       if(oth.guild == GIL_GIANT_BUG)          {   return TRUE;    };
       if(oth.guild == GIL_BLOODFLY)           {   return TRUE;    };
       if(oth.guild == GIL_MINECRAWLER)        {   return TRUE;    };
   };
   // если охотник полевой жук
   if(slf.guild == GIL_GIANT_BUG)
   {
       if(oth.guild == GIL_MEATBUG)            {   return TRUE;    };
       if(oth.guild == GIL_BLOODFLY)           {   return TRUE;    };
       if(oth.guild == GIL_MINECRAWLER)        {   return TRUE;    };
   };
   // если охотник ящерица
   if(slf.guild == GIL_WARAN)
   {
       if(oth.guild == GIL_SCAVENGER)          {   return TRUE;    };
       if(oth.guild == GIL_HARPY)              {   return TRUE;    };
       if(oth.guild == GIL_GOBBO)              {   return TRUE;    };
   };
   // если охотник волк, но не кабан
   if(slf.guild == GIL_WOLF) && (slf.aivar[AIV_MM_REAL_ID] != ID_Keiler)
   {
       if(oth.guild < GIL_SEPERATOR_HUM)       {   return TRUE;    };
       if(oth.guild == GIL_SHEEP)              {   return TRUE;    };
       if(oth.guild == GIL_GIANT_RAT)          {   return TRUE;    };
       if(oth.guild == GIL_MOLERAT)            {   return TRUE;    };
   };
   // если охотник вызванный волк
   if(slf.guild == GIL_SUMMONED_WOLF)
   {
       if(oth.guild == GIL_SHEEP)              {   return TRUE;    };
       if(oth.guild == GIL_GIANT_RAT)          {   return TRUE;    };
       if(oth.guild == GIL_MOLERAT)            {   return TRUE;    };
   };
   // если охотник ползун
   if(slf.guild == GIL_MINECRAWLER)
   {
       if(oth.guild < GIL_SEPERATOR_HUM)       {   return TRUE;    };
       if(oth.guild > GIL_SEPERATOR_ORC)       {   return TRUE;    };
       if(oth.guild == GIL_MEATBUG)            {   return TRUE;    };
       if(oth.guild == GIL_GIANT_BUG)          {   return TRUE;    };
       if(oth.guild == GIL_BLOODFLY)           {   return TRUE;    };
   };
   // если охотник шныг
   if(slf.guild == GIL_LURKER)
   {
       if (oth.guild < GIL_SEPERATOR_HUM)      {   return TRUE;    };
       if (oth.guild > GIL_SEPERATOR_ORC)      {   return TRUE;    };
       if (oth.guild == GIL_GOBBO)             {   return TRUE;    };
   };
   // если охотник зомби
   if (slf.guild == GIL_ZOMBIE)
   {
       if (oth.guild < GIL_SEPERATOR_HUM)      {   return TRUE;    };
       if (oth.guild > GIL_SEPERATOR_ORC)      {   return TRUE;    };
   };
   // если охотник глорх
   if (slf.guild == GIL_SNAPPER)
   {
       if (oth.guild < GIL_SEPERATOR_HUM)      {   return TRUE;    };
       if (oth.guild > GIL_SEPERATOR_ORC)      {   return TRUE;    };
       if (oth.guild == GIL_SHEEP)             {   return TRUE;    };
       if (oth.guild == GIL_GIANT_RAT)         {   return TRUE;    };
       if (oth.guild == GIL_MOLERAT)           {   return TRUE;    };
       if (oth.guild == GIL_SCAVENGER)         {   return TRUE;    };
       if (oth.guild == GIL_HARPY)             {   return TRUE;    };
       if (oth.guild == GIL_WOLF)              {   return TRUE;    };
       if (oth.guild == GIL_SUMMONED_WOLF)     {   return TRUE;    };
       if (oth.guild == GIL_SHADOWBEAST)       {   return TRUE;    };
       if (oth.guild == GIL_GOBBO)             {   return TRUE;    };
       if (oth.guild == GIL_WARAN)             {   return TRUE;    };
       if (oth.guild == GIL_LURKER)            {   return TRUE;    };
   };
   // если охотник мракорис
   if (slf.guild == GIL_SHADOWBEAST)
   {
       if (oth.guild < GIL_SEPERATOR_HUM)      {   return TRUE;    };
       if (oth.guild > GIL_SEPERATOR_ORC)      {   return TRUE;    };
       if (oth.guild == GIL_SHEEP)             {   return TRUE;    };
       if (oth.guild == GIL_GIANT_RAT)         {   return TRUE;    };
       if (oth.guild == GIL_MOLERAT)           {   return TRUE;    };
       if (oth.guild == GIL_SCAVENGER)         {   return TRUE;    };
       if (oth.guild == GIL_HARPY)             {   return TRUE;    };
       if (oth.guild == GIL_WOLF)              {   return TRUE;    };
       if (oth.guild == GIL_SUMMONED_WOLF)     {   return TRUE;    };
       if (oth.guild == GIL_GOBBO)             {   return TRUE;    };
       if (oth.guild == GIL_WARAN)             {   return TRUE;    };
       if (oth.guild == GIL_LURKER)            {   return TRUE;    };
       if (oth.guild == GIL_SNAPPER)           {   return TRUE;    };
   };
   return FALSE;
};


19. Обработчики состояний функций распорядка дня для людей.

Все обработчики расположены в директории ..\AI\Human\TA_Human
// ******************************************
// Объявления Герольда
// ------------------------------------------
// self - герольд, hero - ГГ
// ******************************************

// Функция инициализации состояния
func void ZS_Announce_Herold()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // разрешить восприятие тихих звуков (шаги, звон падающего предмета)
   Npc_PercDisable(self,PERC_ASSESSQUIETSOUND);
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // движение на свой вайпоинт
   AI_GotoWP(self,self.wp);
   // выравнивание на вайпоинте
   AI_AlignToWP(self);
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Announce_Herold_loop()
{
   // если время цикла > 70 сек и ГГ ни с кем не разговаривает
   if(Npc_GetStateTime(self) >= 70) && (hero.aivar[AIV_INVINCIBLE] == FALSE)
   {
       // анимация перехода в состояние чтения
       AI_PlayAni(self,"T_HGUARD_2_STAND");
       // сброс флага
       self.aivar[AIV_TAPOSITION] = NOTINPOS;
       // зачитывание приказов
       B_Announce_Herold();
       // сброс времени цикла
       Npc_SetStateTime(self, 0);
   };
   // если флаг сброшен
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // анимация перехода в обычное состояние
       AI_PlayAni (self,"T_STAND_2_HGUARD");
       // установка флага
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Announce_Herold_end()
{
};

// ***************************************
// Обработчик поведения бандита
// ***************************************

// Функция инициализации состояния
func void ZS_Bandit()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между бандитом и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Bandit_loop()
{
   // если флаг сброшен
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // если бандит не у костра и костер имеется (Примечание: "костер" это ближайший свободный фрипоинт с именем FP_CAMPFIRE_xx, в дальнейшем буду использовать только сокращенное описание)
       if(!Npc_IsOnFP(self,"CAMPFIRE")) && (Wld_IsFPAvailable(self,"CAMPFIRE"))
       {
           // бандит идет к костру
           AI_GotoFP(self,"CAMPFIRE");
           // выравнивается
           AI_AlignToFP(self);
           // садится
           AI_PlayAniBS(self,"T_STAND_2_SIT",BS_SIT);
       }
       // если бандит не на своем месте и место имеется (свободно)
       else if(!Npc_IsOnFP(self,"STAND")) && (Wld_IsFPAvailable(self,"STAND"))
       {
           // бандит идет к месту
           AI_GotoFP(self,"STAND");
           // выравнивается
           AI_AlignToFP(self);
           // охраняет
           AI_PlayAni(self,"T_STAND_2_HGUARD");
       }
       else // иначе
       {
           // выравнивается
           AI_AlignToWP(self);
           // охраняет
           AI_PlayAni(self,"T_STAND_2_HGUARD");
       };
       // установка флага
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   // если прошло 5 сек и флаг установлен и бандит не сидит
   if(Npc_GetStateTime(self) > 5) && (self.aivar[AIV_TAPOSITION] == ISINPOS) && (!C_BodyStateContains(self,BS_SIT))
   {
       var int random;
       random = Hlp_Random(3);
       // с вероятностью 0.33
       if(random == 0)
       {
           // анимация осмотра
           AI_PlayAni(self,"T_HGUARD_LOOKAROUND");
       };
       // сброс времени цикла
       Npc_SetStateTime (self, 0);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Bandit_end()
{
   // если бандит сидит
   if(C_BodyStateContains(self,BS_SIT))
   {
       // встает
       AI_PlayAniBS(self,"T_SIT_2_STAND",BS_STAND);
   }
   else // иначе
   {
       // просто стоит
       AI_PlayAni(self,"T_HGUARD_2_STAND");
   };
};

// ************************************
// Обработчик ритуала в Круге солнца
// ************************************

// Функция инициализации состояния
func void ZS_Circle()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить время реакции на восприятия 0.3 сек
   Npc_SetPercTime(self,0.3);
   // НПС встает
   AI_StandUp(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // движение в Круг солнца
   AI_GotoWP(self,self.wp);
   // выравнивание
   AI_AlignToWP(self);
};

// Функция цикла состояния
func int ZS_Circle_Loop ()
{
   var int randy;
   var int randyKDW;
   var int randyKDW_Lightstar;
   // если НПС черный маг
   if(self.guild == GIL_DMT)
   {
       randy = Hlp_Random(3000);
       // случайное время
       if(Npc_GetStateTime(self) > randy)
       {
           // анимация "Дрожь земли"
           Wld_PlayEffect("FX_EarthQuake",self,self,0,0,0,FALSE);
           // сброс времени цикла
           Npc_SetStateTime(self,0);
           // анимация красного экрана
           Wld_PlayEffect("spellFX_INCOVATION_RED",self,self,0,0,0,FALSE);
           // упражнение в магии
           AI_PlayAni(self,"T_PRACTICEMAGIC5");
       };
   }
   // если НПС маг воды
   else if(self.guild == GIL_KDW)
   {
       randyKDW = Hlp_Random(2500);
       randyKDW_Lightstar= Hlp_Random(2);
       // случайное время
       if(Npc_GetStateTime(self) > randyKDW)
       {
           // выравнивание
           AI_AlignToWP(self);
           // сброс времени цикла
           Npc_SetStateTime(self,0);
           // анимация "Дрожь земли"
           Wld_PlayEffect("FX_EarthQuake",self,self,0,0,0,FALSE );
           // упражнение в магии
           AI_PlayAni(self,"T_PRACTICEMAGIC5");
           // далее какие-то эффекты, кто знает, что делают маги воды в Круге солнца, может написать
           Wld_PlayEffect("spellFX_RingRitual2",ItMi_AmbossEffekt_Addon,ItMi_AmbossEffekt_Addon,0,0,0,FALSE);
           if(randyKDW_Lightstar == 0)
           {
               Wld_PlayEffect("spellFX_RingRitual1",ItMi_AmbossEffekt_Addon,ItMi_AmbossEffekt_Addon,0,0,0,FALSE);
           }
           else if(randyKDW_Lightstar == 1)
           {
               Wld_PlayEffect("spellFX_LIGHTSTAR_RingRitual",ItMi_AmbossEffekt_Addon,ItMi_AmbossEffekt_Addon,0,0,0,FALSE);
           };
       };
   }
   // если квест по восстановлению Глаза Инноса не завершен
   else if(MIS_RitualInnosEyeRepair != LOG_SUCCESS)
   {
       randy = Hlp_Random(1000);
       // случайное время
       if(Npc_GetStateTime(self) > randy)
       {
           // сброс времени цикла
           Npc_SetStateTime(self,0);
           // анимация "Дрожь земли"
           Wld_PlayEffect("FX_EarthQuake",self,self,0,0,0,FALSE);
           // упражнение в магии
           AI_PlayAni(self,"T_PRACTICEMAGIC5");
           // если НПС Ксардас
           if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Xardas))
           {
               // анимация фиолетового экрана
               Wld_PlayEffect("spellFX_INCOVATION_VIOLET",self,self,0,0,0,FALSE);
           }
           // если НПС Пирокар
           else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Pyrokar))
           {
               // анимация красного экрана
               Wld_PlayEffect("spellFX_INCOVATION_RED",self,self,0,0,0,FALSE);
           }
           else // иначе Ватрас
           {
               // анимация голубого экрана
               Wld_PlayEffect("spellFX_INCOVATION_BLUE",self,self,0,0,0,FALSE);
           };
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Circle_End()
{
};

// ***************************************
// Обработчик концерта InExtremo в аддоне
// ***************************************

// Функция инициализации состояния
func void ZS_Concert()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if (Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Concert_Loop ()
{
   // если флаг сброшен
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // если НПС "Yellow Pfeiffer"
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1394_Addon_InExtremo_YellowPfeiffer))
       {
           // использует Желтую волынку
           AI_UseItemToState(self,ItMi_IEDudelGelb,1);
       }
       // если НПС "Dr. Pymonte"
       else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1390_Addon_InExtremo_DrPymonte))
       {
           // использует Арфу
           AI_UseItemToState(self,ItMi_IEHarfe,1);
       }
       // если НПС "The Flail"
       else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1391_Addon_InExtremo_TheFlail))
       {
           // использует Drum
           AI_UseItemToState(self,ItMi_iedrum,1);
       }
       // если НПС "Thomas the forger"
       else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1392_Addon_InExtremo_ThomasTheForger))
       {
           // использует Виолончель
           AI_UseItemToState(self,ItMi_IECello,1);
       }
       // если НПС "Unicorn"
       else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1393_Addon_InExtremo_Unicorn))
       {
           // использует Лютню
           AI_UseItemToState(self,ItMi_IELaute,1);
       }
       // если НПС "Lutter"
       else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1395_Addon_InExtremo_Lutter))
       {
           // использует Drumscheit
           AI_UseItemToState(self,ItMi_IEDrumscheit,1);
       }
       // если НПС "Flex"
       else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1396_Addon_InExtremo_Flex))
       {
           // использует Голубую волынку
           AI_UseItemToState(self,ItMi_IEDudelBlau,1);
       }
       else
       {
            // Танцы пиратов?
       };
       // установка флага
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Concert_End()
{
   // шестерка музыкантов, кроме Flexа, убирают свои инструменты
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1394_Addon_InExtremo_YellowPfeiffer))
   {
       AI_UseItemToState(self,ItMi_IEDudelGelb,-1);
   }
   else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1390_Addon_InExtremo_DrPymonte))
   {
       AI_UseItemToState(self,ItMi_IEHarfe,-1);
   }
   else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1391_Addon_InExtremo_TheFlail))
   {
       AI_UseItemToState(self,ItMi_iedrum,-1);
   }
   else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1392_Addon_InExtremo_ThomasTheForger))
   {
       AI_UseItemToState(self,ItMi_IECello,-1);
   }
   else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1393_Addon_InExtremo_Unicorn))
   {
       AI_UseItemToState(self,ItMi_IELaute,-1);
   }
   else if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(PIR_1395_Addon_InExtremo_Lutter))
   {
       AI_UseItemToState(self,ItMi_IEDrumscheit,-1);
   };
};

// ***************************************
// Обработчик приготовления пищи в котле
// ***************************************

// Функция инициализации состояния
func void ZS_Cook_Cauldron()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если НПС расположен не на своем вайпоинте
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // если НПС не имеет ложки
   if(Npc_HasItems(self,ItMi_Scoop) == 0)
   {
       // создать в инвентаре НПС ложку
       CreateInvItem(self,ItMi_Scoop);
   };
};

// Функция цикла состояния
func int ZS_Cook_Cauldron_Loop()
{
   // если действие с МОВом не прерываемо и котел имеется
   if(!C_BodyStateContains(self,BS_MOBINTERACT_INTERRUPT)) && (Wld_IsMobAvailable(self,"CAULDRON"))
   {
       // мешать ложкой в котле
       AI_UseMob(self,"CAULDRON",1);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Cook_Cauldron_End()
{
   // прекратить использовать котел
   AI_UseMob(self,"CAULDRON",-1);
};

// *******************************************
// Обработчик приготовления пищи на сковороде
// *******************************************

// Функция инициализации состояния
func void ZS_Cook_Pan()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если НПС расположен не на своем вайпоинте
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // если НПС не имеет Сырого мяса
   if(Npc_HasItems(self,ItFoMuttonRaw) == 0)
   {
       // создать в инвентаре НПС Сырое мясо
       CreateInvItem(self,ItFoMuttonRaw);
   };
};

// Функция цикла состояния
func int ZS_Cook_Pan_Loop()
{
   // если действие с МОВом не прерываемо и сковорода имеется
   if(!C_BodyStateContains(self,BS_MOBINTERACT_INTERRUPT)) && (Wld_IsMobAvailable(self,"PAN"))
   {
       // жарить мясо
       AI_UseMob(self,"PAN",1);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Cook_Pan_End()
{
   // прекратить использовать котел
   AI_UseMob(self,"PAN",-1);
   // съесть жареное мясо
   AI_UseItem(self,ItFoMutton);
};

// *************************************
// Обработчик приготовления пищи в печи
// *************************************

// Функция инициализации состояния
func void ZS_Cook_Stove()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если НПС расположен не на своем вайпоинте
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // если НПС не имеет Сырого мяса
   if(Npc_HasItems(self,ItFoMuttonRaw) == 0)
   {
       // создать в инвентаре НПС Сырое мясо
       CreateInvItem(self,ItFoMuttonRaw);
   };
};

// Функция цикла состояния
func int ZS_Cook_Stove_Loop()
{
   // если действие с МОВом не прерываемо и печь имеется
   if(!C_BodyStateContains(self,BS_MOBINTERACT_INTERRUPT)) && (Wld_IsMobAvailable(self,"STOVE"))
   {
       // жарить мясо
       AI_UseMob(self,"STOVE",1);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Cook_Stove_End()
{
   // прекратить использовать печь
   AI_UseMob(self,"STOVE",-1);
   // если НПС имеет больше 1 куска жареного мяса
   if(Npc_HasItems(self,ItFoMutton) >=1 )
   {
       // удалить из инветаря НПС 1 кусок жареного мяса
       Npc_RemoveInvItems(self,ItFoMutton,1);
   };
};

// ***********************
// Обработчик танцев НПС
// ***********************

// Функция инициализации состояния
func void ZS_Dance()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
       // выравнивание
       AI_AlignToWP(self);
   };
};

// Функция цикла состояния
func int ZS_Dance_Loop()
{
   var int danceStyle;
   danceStyle = Hlp_Random(9);
   // случайная анимация фаз танца
   if(danceStyle == 0)
   {
       AI_PlayAni(self,"T_DANCE_01");
   };
   if(danceStyle == 1)
   {
       AI_PlayAni(self,"T_DANCE_02");
   };
   if(danceStyle == 2)
   {
       AI_PlayAni(self,"T_DANCE_03");
   };
   if(danceStyle == 3)
   {
       AI_PlayAni(self,"T_DANCE_04");
   };
   if(danceStyle == 4)
   {
       AI_PlayAni(self,"T_DANCE_05");
   };
   if(danceStyle == 5)
   {
       AI_PlayAni(self,"T_DANCE_06");
   };
   if(danceStyle == 6)
   {
       AI_PlayAni(self,"T_DANCE_07");
   };
   if(danceStyle == 7)
   {
       AI_PlayAni(self,"T_DANCE_08");
   };
   if(danceStyle == 8 )
   {
       AI_PlayAni(self,"T_DANCE_09");
   };
   // движение на свой вайпоинт
   AI_GotoWP(self,self.wp);
   // выравнивание
   AI_AlignToWP(self);
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Dance_End()
{
   // танцор останавливается
   AI_StandUp(self);
};

// **************************************
// Обработчик убегания НПС на свою точку
// **************************************

// Функция инициализации состояния
func void ZS_FleeToWp()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // если НПС Корнелиус
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Cornelius))
   {
       // закрыть восприятие входа в помещение
       Npc_PercDisable(self,PERC_ASSESSENTERROOM);
   };
   // установить режим передвижения бегом
   AI_SetWalkmode(self,NPC_RUN);
   // включить модель убегающего человека
   Mdl_ApplyOverlayMds(self,"HUMANS_FLEE.MDS");
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP (self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // выравнивание
   AI_AlignToWP(self);
   // анимация состояния охраны
   AI_PlayAni(self,"T_STAND_2_HGUARD");
};

// Функция цикла состояния
func int ZS_FleeToWp_Loop()
{
   // если время в состоянии > 5 сек
   if(Npc_GetStateTime(self) > 5)
   {
       var int random;
       random = Hlp_Random(2);
       // с вероятностью 0.5
       if(random == 0)
       {
           // осмотреть окресности
            AI_PlayAni(self,"T_HGUARD_LOOKAROUND");
       };
       // сброс времени состояния
       Npc_SetStateTime(self,0);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func int ZS_FleeToWp_End()
{
};

// *********************************
// Обработчик следования НПС за ГГ
// *********************************

// Функция локального восприятия НПС на ГГ
func void B_AssessFollowPlayer()
{
   // если НПС Диего в Минентале
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(DiegoOW))
   {
       // расстояние между НПС и ГГ < дистанции следования и флаг сброшен (НПС движется)
       if(Npc_GetDistToNpc(self,hero) < self.aivar[AIV_FollowDist]) && (self.aivar[AIV_TAPOSITION] == FALSE)
       {
           // очистить очередь AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает (ждет действий ГГ)
           AI_StandUp(self);
           // установить флаг (НПС остановился)
           self.aivar[AIV_TAPOSITION] = TRUE;
       }
       // если Диего слишком далеко и флаг сброшен
       else if(C_DiegoTooFar(0)) && (self.aivar[AIV_TAPOSITION] == FALSE)
       {
           // очистить очередь AI состояний Диего
           Npc_ClearAIQueue(self);
           // Диего встает (ждет действий ГГ)
           AI_StandUp(self);
           // установить флаг
           self.aivar[AIV_TAPOSITION] = TRUE;
           // если расстояние между Диего и ГГ > дистанции начала разговора
           if(Npc_GetDistToNpc(self,hero) > PERC_DIST_DIALOG)
           {
               // Диего говорит ГГ (SVM фраза) "Подожди!" (Примечание: правильней other заменить на hero)
               B_Say(self,other,"$SC_HEYWAITASECOND");
           };
       }
       else // иначе
       {
           // обычная реакция НПС на ГГ
           B_AssessPlayer();
       };
       return;
   };
   // расстояние между НПС и ГГ < дистанции следования и флаг сброшен
   if(Npc_GetDistToNpc(self,hero) < self.aivar[AIV_FollowDist]) && (self.aivar[AIV_TAPOSITION] == FALSE)
   {
       // очистить очередь AI состояний НПС
       Npc_ClearAIQueue(self);
       // НПС встает (ждет действий ГГ)
       AI_StandUp(self);
       // установить флаг
       self.aivar[AIV_TAPOSITION] = TRUE;
   }
   else
   {
       // обычная реакция НПС на ГГ
       B_AssessPlayer();
   };
};

// Функция локального восприятия НПС на звук сражения
func void B_AssessFollowFightSound()
{
   // если other (нападающий) есть ГГ или victim (жертва) есть ГГ или
   if(Npc_IsPlayer(other)) || (Npc_IsPlayer(victim))
   || (other.aivar[AIV_PARTYMEMBER] == TRUE)           // other - член партии ГГ
   || (victim.aivar[AIV_PARTYMEMBER] == TRUE)          // или victim - член партии ГГ
   {
       // обычная реакция НПС на звук сражения
       B_AssessFightSound();
   };
};

// Данная функция не используется и может быть удалена.
func void B_MoveFollowNpc()
{
};

// Функция инициализации состояния
func void ZS_Follow_Player()
{
   // разрешить НПС все чувства
   self.senses = SENSE_SEE|SENSE_HEAR|SENSE_SMELL;
   // установить дальность чувств 20 метров
   self.senses_range = 2000;
   // установить время реакции на восприятия 0.3 сек
   Npc_SetPercTime(self,0.3);
   // разрешить минимальный набор восприятий
   Perception_Set_Minimal();
   // установить локальное восприятие НПС на ГГ
   Npc_PercEnable(self,PERC_ASSESSPLAYER,B_AssessFollowPlayer);
   // разрешить восприятие врага
   Npc_PercEnable(self,PERC_ASSESSENEMY,B_AssessEnemy);
   // разрешить восприятие разговора
   Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   // разрешить восприятие перемещения МОВа
   Npc_PercEnable(self,PERC_MOVEMOB,B_MoveMob);
   // установить локальное восприятие звуков сражения
   Npc_PercEnable(self,PERC_ASSESSFIGHTSOUND,B_AssessFollowFightSound);
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // НПС встает
   AI_StandUp(self);
   // установить режим передвижения бегом
   AI_SetWalkmode(self,NPC_RUN);
   // если дистанция между НПС не задана
   if(self.aivar[AIV_FollowDist] == 0)
   {
       // задать дистанцию 5м
       self.aivar[AIV_FollowDist] = 500;
   };
};

// Функция цикла состояния
func int ZS_Follow_Player_Loop()
{
   // расстояние между НПС и ГГ > дистанции следования
   if(Npc_GetDistToNpc(self,hero) > self.aivar[AIV_FollowDist])
   {
       // если НПС Диего в Минентале
       if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(DiegoOW))
       {
           // если Диего слишком далеко
           if(C_DiegoTooFar(0))
           {
               // если прошло > 1 сек
               if(Npc_GetStateTime(self) > 1)
               {
                   // Диего поворачивается к ГГ
                   B_TurnToNpc(self,hero);
                   // сброс времени цикла
                   Npc_SetStateTime(self,0);
               };
               return LOOP_CONTINUE;
           };
       };
       // если НПС не плывет
       if(!C_BodyStateContains(self,BS_SWIM))
       {
           // установить режим передвижения бегом
           AI_SetWalkmode(self,NPC_RUN);
       };
       // НПС движется к ГГ
       AI_GotoNpc(self,hero);
       // сброс флага (НПС в движении)
       self.aivar[AIV_TAPOSITION] = FALSE;
   }
   else // расстояние < дистанции следования
   {
       // если прошло > 1 сек
       if(Npc_GetStateTime(self) > 1)
       {
           // НПС поворачивается к ГГ
           B_TurnToNpc(self,hero);
           // сброс времени цикла
           Npc_SetStateTime(self,0);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Follow_Player_End()
{
};

// **********************************
// Обработчик поведения призраков
// **********************************

// Функция инициализации состояния
func void ZS_Ghost()
{
   var c_npc Quarho;
   // получить ссылку на Куарходрона
   Quarho = Hlp_GetNpc(NONE_ADDON_111_Quarhodron);
   // если НПС Куарходрон
   if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Quarho))
   {
       // если информация о "Камерах Аданоса" не известна
       if(Ghost_SCKnowsHow2GetInAdanosTempel == FALSE)
       {
           // разрешить восприятие разговора
           Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
       };
   }
   else
   {
       // разрешить восприятие разговора
       Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   };
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_AssessDamage);
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить время реакции на восприятия 0.1 сек
   Npc_SetPercTime(self,0.1);
   // НПС встает
   AI_StandUp(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // движение на свой вайпоинт
   AI_GotoWP(self, self.wp);
   // выравнивание
   AI_AlignToWP(self);
};

// Функция цикла состояния
func int ZS_Ghost_Loop()
{
   // если прошло >= 5 сек
   if(Npc_GetStateTime(self) >= 5)
   {
       // если расстояние между НПС и ГГ > дистанции начала диалога
       if(Npc_GetDistToNpc(self,hero) > PERC_DIST_DIALOG)
       {
           // выравнивание
           AI_AlignToWP(self);
           // сброс времени состояния
           Npc_SetStateTime(self,0);
       };
       // удалить призрака
       B_RemoveGhost();
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Ghost_End()
{
};

// ******************************
// Обработчик поведения Радемеса
// ******************************

// Функция инициализации состояния
func void ZS_GhostWusel()
{
   // разрешить восприятие разговора
   Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   // разрешить восприятие повреждения
   Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_AssessDamage);
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // НПС встает
   AI_StandUp(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если НПС расположен не на своем вайпоинте
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // движение на свободный фрипоинт
   AI_GotoFP(self,"FP_ROAM");
};

// Функция цикла состояния (Радемес бродит по фрипоинтам)
func int ZS_GhostWusel_Loop()
{
   // если прошло >= 3 сек
   if(Npc_GetStateTime(self) >= 3)
   {
       // если Радемес находится на фрипоинте
       if(Npc_IsOnFP(self,"FP_ROAM"))
       {
           // если есть свободный следующий фрипоинт
           if(Wld_IsNextFPAvailable(self,"FP_ROAM"))
           {
               // очистка очереди AI ссостояний НПС
               Npc_ClearAIQueue(self);
               // переход на следующий фрипоинт
               AI_GotoNextFP(self,"FP_ROAM");
           };
       }
       else // не на фрипоинте
       {
           // если НПС не передвигается
           if(!C_BodyStateContains(self,BS_WALK)) && (!C_BodyStateContains(self,BS_RUN))
           {
               // перейти на фрипоинт
               AI_GotoFP(self,"FP_ROAM");
           };
       };
       // сброс времени состояния
       Npc_SetStateTime(self,0);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_GhostWusel_End()
{
};

// **********************************
// Охрана Святого молота Гарвигом
// **********************************

// Функция инициализации состояния
func void ZS_Guard_Hammer()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Guard_Hammer_loop()
{
   // если НПС находится на фрипоинте охраны
   if(Npc_IsOnFP(self,"STAND"))
   {
       // выравнивание
       AI_AlignToFP(self);
       // если НПС шел на фрипоинт
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   }
   // если фрипоинт существует и свободен
   else if(Wld_IsFPAvailable(self,"STAND"))
   {
       // идти на фрипоинт
       AI_GotoFP(self,"STAND");
       // встать
       AI_StandUp(self);
       // выровняться
       AI_AlignToFP(self);
       // установить флаг перемещения
       self.aivar[AIV_TAPOSITION] = NOTINPOS_WALK;
   }
   else // фрипоинт занят
   {
       // выровняться на вайпоинте
       AI_AlignToWP(self);
       // если НПС шел в точку
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   };
   // если флаг сброшен
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // анимация перехода в режим охраны
       AI_PlayAni(self,"T_STAND_2_HGUARD");
       // установить флаг охраны
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   // если НПС имеет Святой молот и расстояние до места хранения молота < 5м
   if(Npc_HasItems(self,Holy_Hammer_MIS) == 1) && (Npc_GetDistToWP(self,"NW_MONASTERY_SANCTUM_01") <= 500)
   {
       // изъять из инвентаря НПС Святой молот
       Npc_RemoveInvItems(self,Holy_Hammer_MIS,1);
       // положить молот в точку хранения
       Wld_InsertItem(Holy_Hammer_MIS,"FP_HAMMER");
   };
   // если прошло > 5 сек и НПС находится в режиме охраны
   if((Npc_GetStateTime(self) > 5) && (self.aivar[AIV_TAPOSITION] == ISINPOS))
   {
       var int random;
       random = Hlp_Random(10);
       // с вероятностью 0.1
       if(random == 0)
       {
           // осмотреть окрестности
           AI_PlayAni(self,"T_HGUARD_LOOKAROUND");
       };
       // сброс времени цикла
       Npc_SetStateTime(self,0);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Guard_Hammer_end()
{
   // анимация перехода из состояния охраны в стоячее
   AI_PlayAni(self,"T_HGUARD_2_STAND");
};

// ************************
// Охрана прохода НПС
// ************************

// Функция инициализации состояния
func void ZS_Guard_Passage()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll (self);
   // установить время реакции на восприятия 0.1 сек
   Npc_SetPercTime(self,0.1);
   // НПС встает
   AI_StandUp(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // движение на свой вайпоинт
   AI_GotoWP(self,self.wp);
   // если расстояние между НПС и ГГ > дистанции диалога
   if(Npc_GetDistToNpc(self,hero) > PERC_DIST_DIALOG)
   {
       // выровняться
       AI_AlignToWP(self);
   };
};

// Функция цикла состояния
func int ZS_Guard_Passage_Loop()
{
   // если прошло >= 3 сек
   if (Npc_GetStateTime(self) >= 3)
   {
       // если расстояние между НПС и ГГ > дистанции диалога
       if(Npc_GetDistToNpc(self,hero) > PERC_DIST_DIALOG)
       {
           // выровняться
           AI_AlignToWP(self);
           // сброс времени цикла
           Npc_SetStateTime(self,0);
       };
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Guard_Passage_End ()
{
};

// *********************************
// Обработчик следования ГГ за НПС
// *********************************

// Функция локального восприятия НПС на ГГ
func void B_AssessGuidePlayer()
{
   // если расстояние между НПС и ГГ > 8м
   if(Npc_GetDistToNpc(self,hero) > 800)
   {
       // если расстояние от НПС до его вайпоинта < расстояния от ГГ до этого же вайпоинта
       if(Npc_GetDistToWP(self,self.wp) < Npc_GetDistToWP(hero,self.wp))
       {
           // очистить очередь AI состояний НПС
           Npc_ClearAIQueue(self);
           // НПС встает
           AI_StandUp(self);
           // НПС поворачивается к ГГ
           B_TurnToNpc(self,hero);
       };
       // если расстояние между НПС и ГГ > 12м и враги у НПС есть и отношения между НПС и ГГ враждебные
       if(Npc_GetDistToNpc(self,hero) > 1200) && (self.aivar[AIV_EnemyOverride] == TRUE) && (Npc_GetAttitude(self,hero) == ATT_HOSTILE)
       {
           // сброс флага врагов (врагов и НПС нет)
           self.aivar[AIV_EnemyOverride] = FALSE;
       };
   }
   else
   {
       // обычная реакция НПС на ГГ
       B_AssessPlayer();
   };
};

// Функция локального восприятия НПС на звук сражения
func void B_AssessGuideFightSound()
{
   // если other (нападающий) есть ГГ или victim (жертва) есть ГГ
   if(Npc_IsPlayer(other)) || (Npc_IsPlayer(victim))
   {
       // обычная реакция НПС на звук сражения
       B_AssessFightSound();
   };
};

// Функция инициализации состояния
func void ZS_Guide_Player()
{
   // разрешить минимальный набор восприятий
   Perception_Set_Minimal();
   // перевод НПС в исходное состояние
   B_ResetAll (self);
   // разрешить НПС все чувства
   self.senses = SENSE_SEE | SENSE_HEAR | SENSE_SMELL;
   // установить дальность чувств 20 метров
   self.senses_range = 2000;
   // установить время реакции НПС на восприятия 1 сек
   Npc_SetPercTime(self,1);
   // установить локальное восприятие НПС на ГГ
   Npc_PercEnable(self,PERC_ASSESSPLAYER,B_AssessGuidePlayer);
   // разрешить восприятие врага
   Npc_PercEnable(self,PERC_ASSESSENEMY,B_AssessEnemy);
   // разрешить восприятие разговора
   Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
   // разрешить восприятие перемещения МОВа
   Npc_PercEnable(self,PERC_MOVEMOB,B_MoveMob);
   // установить локальное восприятие звуков сражения
   Npc_PercEnable(self,PERC_ASSESSFIGHTSOUND,B_AssessGuideFightSound);
};

// Функция цикла состояния
func int ZS_Guide_Player_Loop()
{
   // если расстояние между НПС и ГГ > 8м и расстояние от НПС до его вайпоинта < расстояния от ГГ до этого же вайпоинта
   if(Npc_GetDistToNpc(self,hero) > 800) && (Npc_GetDistToWP(self,self.wp) < Npc_GetDistToWP(hero,self.wp))
   {
       // локальная реакция НПС на ГГ
       B_AssessGuidePlayer();
   }
   // если расстояние между НПС и его вайпоинтом > 3м
   else if(Npc_GetDistToWP(self,self.wp) > 300)
   {
       // если НПС не плывет
       if(!C_BodyStateContains(self,BS_SWIM))
       {
           // установить режим передвижения бегом
           AI_SetWalkmode(self,NPC_RUN);
       };
       // движение НПС на вайпоинт
       AI_GotoWP(self,self.wp);
   }
   else
   {
       // НПС поворачивается к ГГ
       B_TurnToNpc(self,hero);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func int ZS_Guide_Player_End()
{
};

// *****************************
// НПС справляет малую нужду
// *****************************

// Функция инициализации состояния
func void ZS_Pee()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Pee_loop()
{
   // если НПС на нужном фрипоинте
   if(Npc_IsOnFP(self,"PEE"))
   {
       // выравнивание
       AI_AlignToFP(self);
       // если НПС шел на фрипоинт
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   }
   // если фрипоинт существует и свободен
   else if(Wld_IsFPAvailable(self,"PEE"))
   {
       // идти на фрипоинт
       AI_GotoFP(self,"PEE");
       // встать
       AI_StandUp(self);
       // выровняться
       AI_AlignToFP(self);
       // установить флаг перемещения
       self.aivar[AIV_TAPOSITION] = NOTINPOS_WALK;
   }
   else // фрипоинт занят
   {
       // выровняться на вайпоинте
       AI_AlignToWP(self);
       // если НПС шел в точку
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   };
   // если флаг сброшен и НПС на нужном фрипоинте
   if((self.aivar[AIV_TAPOSITION] == NOTINPOS) && (Npc_IsOnFP(self,"PEE")))
   {
       // выровняться
       AI_AlignToFP(self);
       // встать
       AI_StandUp(self);
       // анимация мочеиспускания
       AI_PlayAni(self,"T_STAND_2_PEE");
       // установить флаг выполнения нужной функции
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Pee_end()
{
   // прекратить мочеиспускание
   AI_PlayAni(self,"T_PEE_2_STAND");
};

// *********************************************************
// НПС бродит по PICK фрипоинтам (что-то ищет или собирает)
// *********************************************************

// Функция инициализации состояния
func void ZS_Pick_FP()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
};

// Функция цикла состояния
func int ZS_Pick_FP_Loop()
{
   // если время состояния >= 7сек и с вероятностью 0.5
   if((Npc_GetStateTime(self) >= 7) && (Hlp_Random(2)))
   {
       // если НПС на фрипоинте и есть еще фрипоинт
       if((Npc_IsOnFP(self,"PICK")) && (Wld_IsFPAvailable(self,"PICK")))
       {
           // НПС идет на следующий фрипоинт
           AI_GotoNextFP(self,"PICK");
           // анимация поиска
           AI_PlayAni(self,"T_PLUNDER");
           // сброс времени цикла
           Npc_SetStateTime(self,0);
       }
       // если НПС на фрипоинте
       else if(Npc_IsOnFP(self,"PICK"))
       {
           // анимация поиска
           AI_PlayAni(self,"T_PLUNDER");
           // сброс времени цикла
           Npc_SetStateTime(self,0);
       };
       // установить режим передвижения шагом
       AI_SetWalkmode(self,NPC_WALK);
   }
   // если время состояния >= 7сек
   else if(Npc_GetStateTime(self) >= 7)
   {
       // если НПС на фрипоинте
       if(Npc_IsOnFP(self,"PICK"))
       {
           // анимация поиска
           AI_PlayAni(self,"T_PLUNDER");
       };
       // установить режим передвижения шагом
       AI_SetWalkmode (self,NPC_WALK);
   };
   // если НПС не на фрипоинте и есть еще фрипоинт
   if((!Npc_IsOnFP(self,"PICK")) && (Wld_IsFPAvailable(self,"PICK")))
   {
       // НПС идет на фрипоинт
       AI_GotoFP(self,"PICK");
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Pick_FP_End()
{
};

// *********************
// НПС добывает руду
// *********************

// Функция инициализации состояния
func void ZS_Pick_Ore()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если НПС расположен не на своем вайпоинте
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // если НПС не имеет кирки
   if(Npc_HasItems(self,ItMw_2H_Axe_L_01) == 0)
   {
       // создать в инвентаре НПС кирку
       CreateInvItem(self,ItMw_2H_Axe_L_01);
   };
   // взять в руки кирку
   EquipItem(self,ItMw_2H_Axe_L_01);
};

// Функция цикла состояния
func int ZS_Pick_Ore_Loop()
{
   // если действие с МОВом не прерываемо и залежи руды имеются
   if(!C_BodyStateContains(self,BS_MOBINTERACT_INTERRUPT)) && (Wld_IsMobAvailable(self,"ORE"))
   {
       // добыча руды
       AI_UseMob(self,"ORE",1);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Pick_Ore_End()
{
   // прекратить добычу руды
   AI_UseMob(self,"ORE",-1);
};

// *****************
// Игра на лютне
// *****************

func void ZS_Play_Lute()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // если НПС не имеет лютни
   if(Npc_HasItems(self,ItMi_Lute) == 0)
   {
       // создать в инвентаре НПС лютню
       CreateInvItem(self,ItMi_Lute);
   };
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Play_Lute_Loop()
{
   // если НПС на нужном фрипоинте
   if(Npc_IsOnFP(self,"STAND"))
   {
       // выравнивание
       AI_AlignToFP(self);
       // если НПС шел на фрипоинт
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   }
   // если фрипоинт существует и свободен
   else if(Wld_IsFPAvailable(self,"STAND"))
   {
       // идти на фрипоинт
       AI_GotoFP(self,"STAND");
       // встать
       AI_StandUp(self);
       // выровняться
       AI_AlignToFP(self);
       // установить флаг перемещения
       self.aivar[AIV_TAPOSITION] = NOTINPOS_WALK;
   }
   else // фрипоинт занят
   {
       // выровняться на вайпоинте
       AI_AlignToWP(self);
       // если НПС шел в точку
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   };
   // если флаг сброшен
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // играть на лютне
       AI_UseItemToState(self,ItMi_Lute,1);
       // установить флаг выполнения нужной функции
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Play_Lute_End()
{
   // прекратить игру на лютне
   AI_UseItemToState(self,ItMi_Lute,-1);
};

// ************************************
// Приготовление алхимических напитков
// ************************************

// Функция инициализации состояния
func void ZS_Potion_Alchemy()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если НПС расположен не на своем вайпоинте
   if(Hlp_StrCmp(Npc_GetNearestWP(self),self.wp) == FALSE)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
   // если НПС не имеет Лабораторной колбы
   if(Npc_HasItems(self,ItMi_Flask) == 0)
   {
       // создать в инвентаре НПС Лабораторную колбу
       CreateInvItem(self,ItMi_Flask);
   };
};

// Функция цикла состояния
func int ZS_Potion_Alchemy_Loop()
{
   // если действие с МОВом не прерываемо и лабораторный стол имеется
   if(!C_BodyStateContains(self,BS_MOBINTERACT_INTERRUPT)) && (Wld_IsMobAvailable(self,"LAB"))
   {
       // изготовление напитка
       AI_UseMob(self,"LAB",1);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Potion_Alchemy_End()
{
   // прекратить алхимию
   AI_UseMob(self,"LAB",-1);
};

// *****************************
// Тренировка магии
// *****************************

// Функция инициализации состояния
func void ZS_Practice_Magic()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
   };
};

// Функция цикла состояния
func int ZS_Practice_Magic_Loop()
{
   // если НПС на нужном фрипоинте
   if(Npc_IsOnFP(self,"STAND"))
   {
       // выравнивание
       AI_AlignToFP(self);
       // если НПС шел на фрипоинт
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK)
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   }
   // если фрипоинт существует и свободен
   else if(Wld_IsFPAvailable(self,"STAND"))
   {
       // идти на фрипоинт
       AI_GotoFP(self,"STAND");
       // встать
       AI_StandUp(self);
       // выровняться
       AI_AlignToFP(self);
       // установить флаг перемещения
       self.aivar[AIV_TAPOSITION] = NOTINPOS_WALK;
   }
   else // фрипоинт занят
   {
       // выровняться на вайпоинте
       AI_AlignToWP(self);
       // если НПС шел в точку
       if(self.aivar[AIV_TAPOSITION] == NOTINPOS_WALK )
       {
           // сброс флага
           self.aivar[AIV_TAPOSITION] = NOTINPOS;
       };
   };
   // если время состояния > 7сек и флаг сброшен
   if((Npc_GetStateTime(self) > 7) && (self.aivar[AIV_TAPOSITION] == NOTINPOS))
   {
       var int zufall;
       zufall = Hlp_Random(100);
       // с разной вероятностью выполнять магические упражнения
       if(zufall <= 25)
       {
           AI_PlayAni(self,"T_PRACTICEMAGIC");
       }
       else if(zufall <= 50)
       {
           AI_PlayAni(self,"T_PRACTICEMAGIC2");
       }
       else if(zufall <= 70)
       {
           AI_PlayAni(self,"T_PRACTICEMAGIC3");
       }
       else if(zufall <= 90)
       {
           AI_PlayAni(self,"T_PRACTICEMAGIC4");
       }
       else
       {
           AI_PlayAni(self,"R_SCRATCHHEAD");
       };
       // сброс времени состояния
       Npc_SetStateTime(self,0);
   };
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Practice_Magic_End()
{
};

// **********************
// Упражнения с мечом
// **********************

// Функция инициализации состояния
func void ZS_Practice_Sword()
{
   // установить нормальный набор восприятий
   Perception_Set_Normal();
   // перевод НПС в исходное состояние
   B_ResetAll(self);
   // установить режим передвижения шагом
   AI_SetWalkmode(self,NPC_WALK);
   // если расстояние между НПС и его вайпоинтом > дистанции выполнения функций
   if(Npc_GetDistToWP(self,self.wp) > TA_DIST_SELFWP_MAX)
   {
       // движение на свой вайпоинт
       AI_GotoWP(self,self.wp);
       // выравнивание
       AI_AlignToWP(self);
   };
   // взять в руки лучшее оружие ближнего радиуса поражения
   AI_EquipBestMeleeWeapon(self);
   // сброс флага цикла
   self.aivar[AIV_TAPOSITION] = NOTINPOS;
};

// Функция цикла состояния
func int ZS_Practice_Sword_Loop()
{
   // если флаг сброшен
   if(self.aivar[AIV_TAPOSITION] == NOTINPOS)
   {
       // если НПС не имеет оружия ближнего радиуса поражения
       if(!Npc_HasEquippedMeleeWeapon(self))
       {
           // создать в инвентаре НПС Тяжелую палку
           CreateInvItems(self,ItMw_1h_Bau_Mace,1);
           // взять её в руки
           AI_EquipBestMeleeWeapon(self);
       };
       // подготовиться
       AI_ReadyMeleeWeapon(self);
       // установка флага готовности
       self.aivar[AIV_TAPOSITION] = ISINPOS;
   };
   // анимация упражнений
   AI_PlayAni(self,"T_1HSFREE");
   // движение на свой вайпоинт
   AI_GotoWP(self,self.wp);
   // выравняться
   AI_AlignToWP(self);
   return LOOP_CONTINUE;
};

// Функция завершения состояния
func void ZS_Practice_Sword_End()
{
   // встать
   AI_StandUp(self);
   // убрать оружие
   AI_RemoveWeapon(self);
};


Уроки скриптологии.

Автор: Vam

Редакция и вёрстка: TycoooN