unit loadz;

interface

Uses
  Controls, sysutils, windows, LevGen, endgame, Graphics, ExtCtrls, Forms, Classes;

type
  byte = integer;
  word = integer;

const
     Black = clBlack;
     Blue = clBlue;
     Green = clGreen;
     Cyan = $00E8E800;
     Red = clRed;
     Magenta = clFuchsia;
     Brown = $00005CB9;
     LightGray = $009D9D9D;//clSilver;
     DarkGray = $00454545;//clGray;
     LightBlue = $00FF8000;
     LightGreen = $0040FF00;
     LightCyan = clAqua;
     LightRed = $008080FF;
     LightMagenta = $00FF80FF;
     Yellow = clYellow;
     White = clWhite;

     RESOLUTION_X = 100;
     RESOLUTION_Y = 50;
     HUDSIZE = 8;

     MAXITERATIONS: integer = 11;
     MAXSTAROSTYEARS = 20;
     HERO_CHAR = '@';
     HERO_COLOR = clGreen;

     TRADER_WEAPONS = 65501;
     TRADER_ARMOR = 65502;
     TRADER_OTHER = 65503;

     MONSTERS_MIN = 1;
     MONSTERS_MAX = 42;

     DEFAULTLIGHTRADIUS = 8;
     MONSTERS_PACK = 8;
     MONSTERS_UNIQUE = 50000;
     NOT_MONSTERS = 60000;
     LEVELUP_MNOZH = 1;
     MINITEMWEAPONINLEVEL = 1;

     ZAPAH_HERO = -1;
     VOICE_HERO = -1;
     VOICE_MOVEABLE = -2;

     MAXVARS = 20;
     MAXITEMSINSLOT = 300;
     SKILL_COUNT = 10;
     MAX_SKILL = 100;
     MAXTRADEITEMS = 40;
     IMPLANTS_COUNT = 11; // Кол-во имплантантов (без учета уровней, то бишь, для скольких характеристик они существуют

     ZT_ROOTS = 1;

     MAP_COLOR = DarkGray;     // Цвет неактивной карты
     MAP_HARD_COLOR = clBlack;   // Цвет неактивной карты на высоком уровне сложности
     GHOST_COLOR = clWhite;      // Цвет героя во время использования заклинания "Призрак"
     INVISIBLE_COLOR = clWhite;  // Цвет героя во время использования заклинания "Невидимость"


     MAXITEMS = 100;
     MAXCOUNT = 200;
     MAXMISSIONS = 1024;
     MAXZAKLS = 60;
     MAXPEREMZ = 1024;
     MAXUSERS = 1000000;
     OSV_ON_MAP = 4;
     SHADOW_COLOR = DarkGray;
     SINCOSCOUNT = 72;

     KEY_HERO_TYPES: array [1..2] of integer = (60010, 64000);

     DATAFOLDER = 'data\';
     MAPFOLDER = DATAFOLDER + 'levels\';
     MAP_MAIN = 'main';
     SAVEFOLDER = DATAFOLDER + 'save\';
     SPEAKFOLDER = DATAFOLDER + 'speak\';
     TEMPFOLDER = DATAFOLDER + 'temp\';
     SAVESDB = SAVEFOLDER + 'saves.dat';
     READFOLDER = DATAFOLDER + 'read\';

     KEY_USE = 'e';
     KEY_INFRARED = 'I';
     KEY_UP = '8';
     KEY_DOWN = '2';
     KEY_LEFT = '4';
     KEY_RIGHT = '6';
     KEY_LEFTUP = '7';
     KEY_LEFTDOWN = '1';
     KEY_RIGHTUP = '9';
     KEY_RIGHTDOWN = '3';
     KEY_READ = 'R';
     KEY_RELOAD = 'r';
     KEY_UNLOAD = 'u';
     KEY_LOAD = 'q';
     KEY_ATTACK = 'f';
     KEY_CHARACTER = 'c';
     KEY_DIG = 'k';
     KEY_INVENTORY = 'i';
     KEY_NOW = 'C';
     KEY_EXIT = 'x';
     KEY_SAVE = 's';
     KEY_SLEEP = 'y';
     KEY_MISS = 'g';
     KEY_TIME = 'T';
     KEY_LEVELUP = 'l';
     KEY_CLOSE = 'j';
     KEY_SCREENSHOT_HTML = 'S';
     KEY_SCREENSHOT_TXT = #8;
     KEY_RETURN = #13;
     KEY_ESCAPE = #27;
     KEY_BACKSPACE = #8;
     KEY_INV_DROP = 'd';
     KEY_INV_STRENGTH_DROP = 'D';
     KEY_PRISON_DOWN = '>';
     KEY_PRISON_UP = '<';
     KEY_HELP = '?';
     KEY_HACK = 'H';
     KEY_PASS_INPUT = 'i';
     KEY_HEAR = 'h';
     KEY_SMELL = 'n';
     KEY_INV_HELP = 'h';
     KEY_LAST_MESSAGES = '`';
     KEY_LOOKMODE = 'L';
     KEY_SHOOT = ' ';
     KEY_SUICIDE = 'O';
     KEY_NEUTRALIZE_TRAP = 'N';

     SC_GO = 1;
     SC_PUT = 2;
     SC_SHOOT = 3;
     SC_TOUCH = 4;
     SC_ITEM = 5;
     SC_TALK_BEGIN = 6;
     SC_TALK_END = 7;
     SC_ONCE = 128;

     MONSTERS_IN_WORLD = 1000;
     FRIEND = 60000;
     {
       Помимо номеров в списке будут еще уникальные вещи,
       список с диапазонами нужен только для продавцов
       и подобных: а также для генерации подземелий
     }
     WEAPON_MIN = 1;
     WEAPON_MAX = 42;
     WEAPON_PRIMITIVE_MIN = 1;
     WEAPON_PRIMITIVE_MAX = 15;
     SELL_PRIMITIVE_MAX = 13;
     WEAPON_SHOOT_MIN = 16;
     WEAPON_SHOOT_MAX = 45;
     WEAPON_BOWS_MIN = 16;
     WEAPON_BOWS_MAX = 18;
     SELL_FIRE_MAX = 23;
     WEAPON_FIRE_MIN = 19;
     WEAPON_FIRE_MAX = 25;
     SELL_SHELLS_MAX = 28;
     WEAPON_SHELLS_MIN = 26;
     WEAPON_SHELLS_MAX = 29;
     WEAPON_NAPALM_MIN = 30;
     WEAPON_NAPALM_MAX = 31;
     WEAPON_ELECTRIC_MIN = 32;
     WEAPON_ELECTRIC_MAX = 35;
     WEAPON_ROCKETS_MIN = 36;
     WEAPON_ROCKETS_MAX = 38;
     WEAPON_CELL_MIN = 39;
     WEAPON_CELL_MAX = 42;
     WEAPON_PSI_MIN = 43;
     WEAPON_PSI_MAX = 45;
     ARMOR_MIN = 46;
     ARMOR_MAX = 68;
     IMPLANTS_MIN = 70;
     IMPLANTS_MAX = 103;
     BOOKS_MIN = 280;
     BOOKS_MAX = 360;
     ZELYE_MIN = 361;
     ZELYE_MAX = 368;
     EAT_MIN = 401;
     EAT_MAX = 410;
     MEDIC_MIN = 411;
     MEDIC_MAX = 417;
     KEY_MIN = 418;

     ITEM_IRGLASSES = 1515;     // Инфракрасные очки
     ITEM_HACKGADGET = 420;    // Устройство для взлома кодовых замков
     ITEM_LOPATA = 421;        // Лопата
     {
        422 - ломик Гордона Фримена
        423 - Электродрель
        424 - Бензопила
        425 - Световой меч
        426 - Арбалет Темного Лорда
        427 - Миниган
        428 - BFG 9000
        429 - 433 - ключ-карты для подземелий
        434 - ключ от уборной в гостинице (Сойя)
     }

     KEY_MAX = 600;
     MONEY_MIN = 601;
     MONEY_MAX = 605;
     AMMO_MIN = 606;
       AMMO_BOWS = 606;
       AMMO_FIRE = 607;
       AMMO_SHELLS = 608;
       AMMO_NAPALM = 609;
       AMMO_SNIPER = 610;
       AMMO_ELECTRICAL = 611;
       AMMO_ROCKETS = 612;
       AMMO_CELLCHARGE = 613;
       AMMO_PSI = 614;
     AMMO_MAX = 614;
     TECHNO_MIN: integer = 615;
     TECHNO_MAX: integer = 638;
     TECHNO_BAD: integer = 630;
     FLOPPY_MIN: integer = 641;
     FLOPPY_MAX = 665;
     FLOPPY_MAX2: integer = 665;
     FLOPPY_BAD: integer = 657;
     DRINK_MIN: integer = 666;
     DRINK_MAX: integer = 667;//710;
     DRINK_BAD: integer = 700;
     LITTER_MIN = 1000;
     LITTER_MAX = 1013;
     DRAGSTONE_MIN = 1500;
     DRAGSTONE_MAX = 1513;
     RARE_MIN = 1514;
     RARE_MAX = 1515;
     LITTER_PRISON_MIN = 2000;
     LITTER_PRISON_MAX = 2005;

     ITEM_1_MIN = 1;
     ITEM_1_MAX = KEY_MIN - 1;
     ITEM_2_MIN: integer = MONEY_MAX + 1;
     ITEM_2_MAX = FLOPPY_MAX;

     MT_INFO = 1;
     MT_WARNING = 2;
     MT_ERROR = 3;
     MT_TEXT = 0;
     MT_COOL = 4;
     MT_CRITICAL = 5;
     MT_ITEM = 6;

     DT_MORNING = 1;
     DT_DAY = 2;
     DT_EVENING = 3;
     DT_NIGHT = 4;

     SPEAK_MAINLOC = 'main';
     LEV_EXTENSION = '.lev';
     SAZ_EXTENSION = '.saz';
     SAV_EXTENSION = '.sav';
     SAVEFILENAME = 'game.sav';
     ARC_SAVEFILENAME = 'save.dat';
     SPEAKTEMPFILE = 'speak.tmp';
     SPEAKNOWFILE = 'speak.now';
     FONTFILENAME = 'font.dat';

     INC_MAXWEIGHT = 400;
     INC_MAXHEALTH = 5;

const                                        // Строковые константы
     STR_PRESS_ESCAPE = ' S - начало игры. Распределить очки можно в любой момент игры. ';
     STR_YOUR_POINTS = 'Очки опыта: ';
     STR_YOUR_HEALTH = 'Здоровье: ';
     STR_YOUR_WEIGHT = 'Переносимый вес: ';
     STR_MAX_WEIGHT = 'Макс. вес: ';
     STR_UNAVAILABLE = '0';
     STR_YEARSOLD = ' лет. ';
     STR_YEARAOLD = ' года. ';
     STR_YEAROLD = ' год. ';
     STR_BIRTHDATE = 'Дата рождения: ';
     STR_YEARZ = ' г.';
     STR_CLASS = 'Класс: ';
     STR_HEALTH = 'Здоровье:';
     STR_PRISONLEVEL = 'Уровень: ';
     STR_MANA = 'Мана:';
     STR_UNDERCLASS = 'Подкласс: ';
     STR_MONEY = 'Золото: ';
     STR_WEIGHT = 'Вес: ';
     STR_LEVEL = 'Уровень: ';
     STR_EXPA = 'Опыт: ';
     STR_EXPA2NEXT = 'Нужно для следующего уровня: ';
     STR_CURSE = 'Проклят!';
     STR_VERY_VERY_TIRED = 'Валится с ног!';
     STR_VERY_TIRED = 'Очень устал!';
     STR_TIRED = 'Устал!';
     STR_VERY_VERY_HUNGRY = 'Умирает от голода!';
     STR_VERY_HUNGRY = 'Очень голоден!';
     STR_HUNGRY = 'Голоден!';
     STR_HUD_USTAL = 'Усталость';
     STR_HUD_HUNGRY = 'Голод';
     STR_HUD_BLOODCHOSEN = 'Кровотечение';
     STR_HUD_BOLEZN = 'Болезнь';
     STR_HUD_BLIND = 'Слепота';
     STR_HUD_ED = 'Глухота';
     STR_HUD_POISONED = 'Отравление';
     STR_HUD_CONTUSIONED = 'Контузия';
     STR_HUD_PALSY = 'Паралич';
     STR_HUD_STRAH = 'Испуг';
     STR_YET_VERY_VERY_HUNGRY = 'Вы по-прежнему умираете от голода';
     STR_YET_VERY_HUNGRY = 'Вы все еще чувствуете себя очень голодным';
     STR_YET_HUNGRY = 'Чувство голода все еще дает о себе знать';
     STR_NOT_HUNGRY = 'Чувство голода утихло';
     STR_FULL_HUNGRY = 'Вы чувствуете себя сытым';
     STR_YET_BLOOD = 'Вы продолжаете истекать кровью';
     STR_NO_BLOOD = 'Кровотечение из раны прекратилось';
     STR_YOU_DRINK = 'Вы утолили жажду';
     STR_FEEL_NOT = 'Вы ничего не учвствуете';
     STR_FEEL_ENERGY_POPOL = 'Вы чувствуете прилив энергии';
     STR_NOT_BOLEN = 'Вы чувствуете себя здоровым - болезнь отступила';
     STR_YOU_FEEL_NOT = 'Вы ничего не чувствуете';
     STR_YOU_HEALTH_MAX = 'Ваше здоровье и так на максимуме';
     STR_YOU_FEEL_BETTER = 'Вы чувствуете себя лучше';
     STR_YOU_ABSOLUTELY_HEALTH = 'Вы полностью здоровы';
     STR_NOT_ILL = 'Вы и так здоровы';
     STR_NOT_BLOODCHOSEN = 'У вас нет кровотечения';
     STR_VERY_VERY_ILL = 'Смертельно болен!';
     STR_VERY_ILL = 'Сильно болен!';
     STR_ILL = 'Болен!';
     STR_VERY_VERY_BLOODCHOSEN = 'Истекает кровью!';
     STR_VERY_BLOODCHOSEN = 'Сильное кровотечение!';
     STR_BLOODCHOSEN = 'Кровотечение!';
     STR_SCARED = 'Испуган';
     STR_CLOSED = 'Закрыто';
     STR_CLOSED_RED = 'Закрыто на красный магнитный замок';
     STR_CLOSED_BLUE = 'Закрыто на синий магнитный замок';
     STR_CLOSED_GREEN = 'Закрыто на зеленый магнитный замок';
     STR_CLOSED_GRAY = 'Закрыто на серый магнитный замок';
     STR_CLOSED_MAGENTA = 'Закрыто на фиолетовый магнитный замок';
     STR_IMPOSSIBLE_TO_GRAB = 'Невозможно перебраться через окно';
     STR_YOU_GRAB_THROUGH_WINDOW = 'Вы перелезли через оконную раму';
     STR_YOU_WAS_KILLED = 'В Ы    Б Ы Л И    У Б И Т Ы    В О    С Н Е . . .';
     STR_DEAD = 'В Ы    У Б И Т Ы . . .';
     STR_KG = ' кг';
     STR_UMER_STAROST = 'Годы взяли свое - вы умерли от старости.';
     STR_WINDOW_CRASHED = 'Осколки разлетелись вокруг - вы разбили окно';
     STR_GLASS_BOLD = 'Стекло слишком прочное, чтобы чем-то разбить его';
     STR_YOU_FEEL_HURT = 'Вы попали в ловушку! Вы чувствуете боль';
     STR_YOU_FEEL_HURT_POISON = 'Вы попали в отравленную ловушку! Вы чувствуете боль';
     STR_YOU_TRAPPED_BLIND = 'Вы попали в ловушку! Яркая вспышка ослепляет вас...';
     STR_YOU_TRAPPED_ED = 'Вы попали в ловушку! Нестерпимый грохот откуда-то неподалеку оглушает вас...';
     STR_YOU_TRAPPED_CONTUSIONED = 'Вы попали в ловушку! От сильного удара по голове у вас начинается контузия...';
     STR_YOU_TRAPPED_TELEPORT = 'Вы попали в ловушку! Вы чувствуете, как летите куда-то сквозь пространство...';
     STR_YOU_TRAPPED_DOWN = 'Вы попали в ловушку! Земля уходит у вас из-под ног, вы летите вниз...';
     STR_YOU_GO_TO_WALL = 'Вы уперлись в стену';
     STR_YOU_CANT_GO_BY_MOUNTAINS = 'Вам не пройти по этим горам';
     STR_YOU_CANT_GO_HERE = 'Здесь невозможно пройти';
     STR_CANT_GO_THIS_LITTER = 'Здесь куча мусора, вам не пройти';
     STR_CANT_GO_THIS_LAND = 'Здесь куча земли, вам не пройти';
     STR_YOU_SWOONED = 'От усталости вы упали в обморок...';
     STR_YOU_AWAKENED = 'Вы очнулись...';
     STR_YOU_AWAKENED2 = 'Вы очнулись';
     STR_YOU_HUNGRY = 'Вы чувствуете голод';
     STR_YOU_VERY_HUNGRY = 'Чувство голода многократно усилилось';
     STR_YOU_VERY_VERY_HUNGRY = 'Вы валитесь с ног от голода...';
     STR_BLOODCHOSEN_CONTINIE = 'Кровотечение усиливается...';
     STR_BLOODCHOSEN_VERY = 'Вы истекаете кровью!';
     STR_BLOODCHOSEN_STOP = 'Внезапно кровотечение остановилось';
     STR_YOU_TIRED = 'Вы чувствуете усталость...';
     STR_YOU_VERY_TIRED = 'Ваши ноги подкашиваются от усталости';
     STR_YOU_VERY_VERY_TIRED = 'Кажется, сейчас вы упадете в обморок...';
     STR_YOU_FEARED = 'Вы боитесь атаковать!';
     STR_YOU_FEARED_FIRE = 'Ваши руки дрожат от страха, вы не можете прицелиться';
     STR_DOOR_BROKEN = 'Не могу закрыть дверь';
     STR_EXIT_QUESTION = 'Выйти? (y/n)';
     STR_CHANGESSAVE_QUESTION = 'Сохранить изменения? (y/n)';
     STR_DIG_DIRECTION_QUESTION = 'В каком направлении копать?';
     STR_TRAP_DIRECTION_QUESTION = 'Ловушку в каком направлении попытаться обезвредить?';
     STR_I_HAVENOT_SHOVEL = 'У меня нет лопаты!';
     STR_NOTHING_TO_DIG = 'Здесь нечего копать!';
     STR_NOTHING_TO_NEUTRALIZE = 'Поблизости не видно ловушек';
     STR_YOU_NOT_NEUTRALIZE_TRAP = 'Обезвреживание не удалось';
     STR_YOU_GO_TO_SLEEP = 'Вы легли поспать...';
     STR_YOU_SLEPT1 = 'Похоже, Вы проспали около ';
     STR_YOU_SLEPT2 = ' час';
     STR_YOU_SLEPT3 = '...';
     STR_BLIND_PROTECTION = 'Сопротивление слепоте';
     STR_FEAR_PROTECTION = 'Сопротивление страху';
     STR_CONTUSION_PROTECTION = 'Сопротивление контузии';
     STR_PALSY_PROTECTION = 'Сопротивление параличу';
     STR_POISON_PROTECTION = 'Сопротивление яду';
     STR_GLUK_PROTECTION = 'Сопротивление глюкам';
     STR_USED_IMPLANT = 'Установлен ';
     STR_ITEM = 'Предмет (';
     STR_DESTROYED = ') уничтожен';
     STR_YOU_PASSED_TURN = 'Вы пропустили ход';
     STR_SCREENSHOT_SAVED = 'Скриншот сохранен';
     STR_DIRECTION_QUESTION = 'В каком направлении?';
     STR_CHOOSE_YOUR_HERO_UNDERCLASS = 'Выберите подкласс вашего героя.';
     STR_ESC_CANCEL = '''Esc'' - назад.';
     STR_NOT_ENOUGH_MONEY = 'Не хватает денег';
     STR_YOU_ALREADY_HAVE_IMPLANT = 'У вас уже установлен этот имплантант.';
     STR_SURGERY_TOO_SMALL = 'Ваших навыков в хирургии недостаточно для установки имплантанта';
     MenuItemsCount = 5;
     STR_MENUARR: array[1..MenuItemsCount] of string =
     (
       'Новая игра',
       'Загрузить игру',
       'Помощь',
       'Об игре',
       'Выход'
     );
     CreditsCount = 13;
     STR_CREDITSARR: array[1..CreditsCount] of string =
     (
       '+======================================+',
       '                                      ',
       ' В разработке игры принимали участие: ',
       '                                      ',
       '          Программирование:           ',
       '              NEME$iS                 ',
       '            Shad0wDeaTh               ',
       '                                      ',
       '              Дизайн:                 ',
       '              Empresa                 ',
       '              NEME$iS                 ',
       '                                      ',
       '+======================================+'
     );
     STR_OPENED = 'Открыто';
     STR_QUESTION_ALL_DATA_WILL_BE_DELETED = 'Все сохраненные данные будут потеряны! Продожить? (Y/N)';
     STR_NOTHING_TO_DROP = 'Нечего бросать!';
     STR_CANT_DROP_HERE = 'Не могу выбросить это здесь';
     STR_POPAD_HEAD = 'в голову';
     STR_POPAD_BODY = 'по корпусу';
     STR_POPAD_HANDS = 'в руку';
     STR_POPAD_LEGS = 'в ногу';
     STR_RANIL_YOU = ' ранил вас на ';
     STR_RANILA_YOU = ' ранила вас на ';
     STR_RANILO_YOU = ' ранило вас на ';
     STR_MISS = 'промахнулся';
     STR_MISS_SHE = 'промахнулась';
     STR_MISS_IT = 'промахнулось';
     STR_RANILO = 'Вы ранены на ';
     STR_POINTS = ' единиц';
     STR_NOT_RANIL_YOU = 'Вы перенсли ранение без последствий';
     STR_YOU_ARE_ILL = 'Вам становится плохо... похоже, вы заболели';
     STR_YOU_ILL_MORE = 'Вы чувствуете себя хуже - болезнь усиливается';
     STR_ARE_BLOODCHOSEN = 'Кровотечение из раны не прекращается...';
     STR_YOU_ARE_PROKLYAT = 'Вас прокляли!';
     STR_YOU_FOUND = 'Вы нашли ';
     STR_N_MONET = ' монет';
     STR_YOU_DONT_FOUND_MONEY = 'Вы не нашли золота';
     STR_AND = ' и ';
     STR_BUT_FOUND = ', но нашли ';
     STR_YOUR_INVENTORY_FULL_CANT_PUT_ITEM = ', ваш инвентарь полон, вы не можете взять вещь при нем';
     STR_WAS_FOUND = 'Подобрано: ';
     STR_N_MONETS2 = ' монет';
     STR_DEATH = ' убит';
     STR_DEATH_FEMALE = ' убита';
     STR_DEATH_OTHER = ' убито';
     STR_DISAPPEAR_DEATH = ' растворяется в воздухе';
     STR_LOPNUL_DEATH = ' разлетается на части';
     STR_INVENTORY_FULL = 'Инвентарь переполнен!';
     STR_I_UBIT = 'и убит';
     STR_I_UBITA = 'и убита';
     STR_I_UBITO = 'и убито';
     STR_POINTS2 = ' единиц';
     STR_YOU_ARE_MISSED = 'Вы промахнулись';
     STR_HAVENT_AMMO = 'Нечем стрелять!';
     STR_HAVENT_AMMO_IN_INV = 'В рюкзаке нет зарядов';
     STR_IMPOSSIBLE_RELOAD = 'Это оружие невозможно перезарядить';
     STR_UNLOAD_IMPOSSIBLE = 'Это оружие невозможно разрядить';
     STR_WEAPON_ALREADY_EMPTY = 'Оружие уже разряжено';
     STR_OBOYMA_FULL = 'Оружие уже полностью заряжено';
     STR_NEED_RELOAD = 'Оружие необходимо перезарядить!';
     STR_YOU_HAVENT_SHOOTING_AMMO_IN_HANDS = 'У меня в руках нет стреляющего оружия';
     STR_SHOOT_MYSELF = 'Эй, я пока еще не совсем дурак!';
     STR_I_WILL_NOT_SHOOT_AT_THEM = 'Я не собираюсь стрелять в них';
     STR_AND_KILLED = ' и убит';
     STR_BOUGHT = 'Продано!';
     STR_AND_KILLED_FEMALE = ' и убита';
     STR_AND_KILLED_OTHER = ' и убито';
     STR_RANEN_NA = ' был ранен на ';
     STR_RANENA_NA = ' была ранена на ';
     STR_RANENO_NA = ' было ранено на ';
     STR_POINTS3 = ' единиц';
     STR_NOT_RANEN = ' перенес ранение без последствий';
     STR_NOT_RANENA = ' перенесла ранение без последствий';
     STR_NOT_RANENO = ' перенесло ранение без последствий';
     STR_YOU_MISSED2 = 'Вы промахнулись   ';
     STR_JANUARY = 'января ';
     STR_FEBRUARY = 'февраля ';
     STR_MARCH = 'марта ';
     STR_APRIL = 'апреля ';
     STR_MAY = 'мая ';
     STR_JUNE = 'июня ';
     STR_JULE = 'июля ';
     STR_AUGUST = 'августа ';
     STR_SEPTEMBER = 'сентября ';
     STR_OCTOBER = 'октября ';
     STR_NOVEMBER = 'ноября ';
     STR_DECEMBER = 'декаря ';
     STR_LEVELUP = 'Вы достигли следующего уровня!';
     STR_CANT_MOVE = 'Не могу двинуть!';
     STR_RASKOPAL = 'Вы раскопали...';
     STR_GAME_SCREENSHOT = 'Game screenshot (';
     STR_INVALID_CODE = 'Неверный код!';
     STR_INPUT_CODE = 'Введите код: ';
     STR_CORRECT_CODE = 'Код верен!';
     STR_YOU_SMELL = 'Вы чуете ';
     STR_YOU_HEAR = 'Вы слышите: поблизости ';
     STR_YOU_HEAR_NOTHING = 'Вы не слышите ничего необычного';
     STR_YOU_SMELL_NOTHING = 'Вы не чувствуете ничего особенного';
     STR_MIN_SKILL = 'Навык: ';
     STR_R_HAND = 'Оружие: ';
     STR_L_HAND = 'Щит: ';
     STR_TROUSERS = 'На ногах: ';
     STR_GLOVES = 'На руках: ';
     STR_AMULET = 'Амулет: ';
     STR_YOUR_GOLD = 'Золото: ';
     STR_DOSP = 'Доспехи: ';
     STR_SAPOGI = 'Обувь: ';
     STR_SHAPKA = 'На голове: ';
     STR_PLASHCH = 'За спиной: ';
     STR_BRACELET1 = 'На правой руке: ';
     STR_BRACELET2 = 'На левой руке: ';
     STR_BUY_1 = '[ Пожалуй, я дам тебе ';
     STR_BUY_2 = ' монет';
     STR_BUY_3 = ' за это ]';
     STR_WHAT_YOU_WANT = 'Что тебе нужно, путник?';
     STR_YOU_FOUND_SECRET_DOOR = 'Найдена секретная дверь!';
     STR_ITEM_DESTROYED = 'Предмет исчез в снопе искр...';
     STR_ARE_YOU_WANT = 'Нанокод был использован. Вы действительно хотите выйти?(Y/N)';
     STR_GLUK_DISAPPEARED = ' растворился в клубах темноты...';
     STR_WHAT: Array[1..6] of String =
     (
       'У меня есть кое - какое барахло на продажу',
       'Посмотрим, какое оружие у тебя есть',
       'Что ты можешь предположить из доспехов?',
       'Посмотрим, что у тебя есть...',
       'Я болен, вылечи меня',
       'Нет, ничего, просто проходил мимо'
     );
     STR_THIS_COST_1 = 'Что ж, я вылечу тебя за ';
     STR_THIS_COST_2 = ' монет. Соглашаешься? (Y/N)';
     STR_HEALTH_SUCCESS = 'Готово, теперь ты здоровее быка';
     STR_NOT_HEALTH_MONEY = 'У тебя не хватит денег. Я не лечу бесплатно, извини';
     STR_YOU_ALREADY_HEALTH = 'Хм, судя по твоему виду, лечение тебе не нужно.';
     STR_HAVENT_GLASSES = 'У меня нет очков';
     STR_LAST_MESSAGES = 'Последние сообщения';
     STR_GRIB_ATTACK = ' выбрасывает ядовитые споры...';
     STR_BOMZH_NOT_STEAL = ' пытается обокрасть вас. Вы вовремя замечаете это.';
     STR_CANT_FIND_NAME = 'Не могу найти сейвы для игрока ';
     STR_BOLEZN_STONGER = 'Болезнь дает о себе знать - вы чувствуете себя хуже';
     STR_BOLEZN_AWAY = 'Вы чувствуете себя здоровым - похоже, болезнь отступила';
     STR_RANDOM_PRISON = 'Пещера в скале';
     STR_OSM_VOKRUG = 'Осматриваемся вокруг...';
     STR_POBL_OSM = 'Кажется, поблизости кто-то есть. Осматриваемся...';
     STR_DOWN_TO_LEVEL = 'Спускаемся на уровень ';
     STR_UP_TO_LEVEL = 'Поднимаемся на уровень ';
     STR_UP = 'Поднимаемся наверх...';
     STR_EXIT_FROM_LOCATION = 'Выходим с локации...';
     STR_ENTER_TO_LOC = 'Входим в локацию...';
     STR_SKILL_STRENGTH = 'Сила';
     STR_SKILL_INTELLECT = 'Интеллект';
     STR_SKILL_LUCK = 'Удача';
     STR_SKILL_STAMINA = 'Выносливость';
     STR_SKILL_ATTENTION = 'Внимание';
     STR_SKILL_AGILITY = 'Ловкость';
     STR_SKILL_ACCURACY = 'Меткость';
     STR_SKILL_PROTECT_BASE = 'Общая защита';
     STR_SKILL_PROTECT_FIRE = 'Защита от огнестрельного оружия';
     STR_SKILL_PROTECT_ELECTRIC = 'Защита от электро-оружия';
     STR_SKILL_PROTECT_PSI = 'Защита от пси-оружия';
     STR_SKILL_WEAPON = 'Владение оружием';
     STR_SKILL_RESISTANCE = 'Сопротивляемость';
     STR_ESC_BACK = '''Esc'' - назад.';
     STR_CANT_CARRY_ANYTHING = 'Больше я не смогу уенсти';
     STR_HELP_ATTACK = 'Урон: ';
     STR_HELP_DEFENCE = 'Защита: ';
     STR_HELP_OTHER = 'Эффект: ';
     STR_HELP_COST = 'Стоимость: ';
     STR_HELP_WEIGHT = 'Вес: ';
     STR_CANT_USE_THIS = '          Не могу это использовать';
     STR_COST_1 = 'Пожалуй, я продам тебе это всего за ';
     STR_COST_2 = ' монет';
     STR_INPUT_COUNT = ' Количество: ';
     STR_INCORRECT_INPUT = 'Некорректный ввод!';
     STR_I_HAVENT_ENOUGH = 'У меня нет столько!';
     STR_I_HAVENT_ENOUGH_MONEY = 'У меня нет столько денег';
     STR_I_HAVENT_INV = 'Я не смогу унести столько';
     STR_ERROR_OPENING_FILE = 'Не могу открыть файл ';
     STR_CANT_OPEN_SAVEGAME = 'Не могу открыть файл сохранения';
     STR_CRITICAL_ERROR = 'Критическая ошибка: ';
     STR_YOU_OPEN_CHEST = 'Вы открыли сундук';
     STR_GAME_SAVED = 'Игра сохранена';
     STR_STATUE_LABEL = 'Надпись на статуе' + #13#10;
     STR_FILE_NOT_FOUND = 'Файл с необходимыми данными не найден!';
     STR_EXPA_1 = '[ Очки опыта: ';
     STR_EXPA_2 = ' ] (Нажмите Enter, чтобы повысить выбранный скилл)';
     STR_EXPA_2_WITHOUT = ' ]';
     STR_TOO_HEAVY = 'Слишком тяжело';
     STR_IN_WHAT_DIRECTION = 'В каком направлении?';
     STR_DONT_HACK = 'Поблизости нет запертых дверей';
     STR_I_HAVE_NOT_HACKGADGET = 'У вас нет электронного взломщика';
     STR_CONTENT_GETTING = 'Получение содержимого';
     STR_PZU_CHIP = 'ПЗУ - чипа';
     STR_YACHEIKA = 'Ячейка ';
     STR_FROM = ' из ';
     STR_INPUT_THE_KEY = 'Введите ключ: ';
     STR_UNCRYPTING = 'Расшифровка';
     STR_UNDER_LEGS = 'Что-то звякнуло у вас под ногами...';
     STR_CODE_GOT = 'Получен код: ';
     STR_CHECKING = 'Проверка';
     STR_ERROR_404 = 'Ошибка - введенный код неверен!';
     STR_STRING_GOT = '     Строка получена     ';
     STR_SUCCESS = '         Успех!          ';
     STR_GHOSTS_CANT_USE_LADDERS = 'Вы не можете покинуть локацию в режиме разведки';
     STR_GHOSTS_CANT_USE_IRGLASSES = 'Вы не можете использовать очки в режиме разведки';
     STR_GHOSTS_CANT_USE_SHOVELS = 'Вы не можете копать в режиме разведки';
     STR_GHOSTS_CANT_USE_HACKGADGET = 'Вы не можете использовать взломщик в режиме разведки';
     STR_GHOSTS_CANT_PICKUP = 'Вы не можете собирать вещи в режиме разведки';
     STR_GHOSTS_CANT_SLEEP = 'Вы не можете спать в режиме разведки';
     STR_GHOSTS_CANT_USE_INVENTORY = 'Инвентарь недоступен в режиме разведки';
     STR_NOT_INTERESTED = 'Меня это не интересует';
     STR_PICKUP = ' поднимает ';
     STR_BLIND = 'Ослеп!';
     STR_ED = 'Оглох!';
     STR_POISONED = 'Отравлен!';
     STR_CONTUSIONED = 'Контужен!';
     STR_PALSIED = 'Парализован!';
     STR_NOT_BLIND = 'Вы снова можете видеть';
     STR_NOT_ED = 'Вы снова можете слышать';
     STR_NOT_POISONED = 'Яд полностью вышел из организма';
     STR_NOT_CONTUSIONED = 'Вы больше не контужены';
     STR_NOT_PALSED = 'Вы снова можете двигаться';
     STR_YOU_BLIND = 'Вы ослепли!';
     STR_YOU_ED = 'Вы оглохли!';
     STR_YOU_POISONED = 'Вы отравлены!';
     STR_YOU_CONTUSIONED = 'Вы контужены!';
     STR_YOU_PALSED = 'Вы парализованы!';
     STR_YOU_VOMIT = 'Яд дает о себе знать - вас рвет';
     STR_YOU_CANT_ESCAPE = 'Вы не можете уйти с локации, пока рядом находятся монстры';
     STR_CANT_DROP_PROKLYAT = 'Браслет не поддается - вы не можете снять его';
     STR_SORRY_NOT_INTERESTED = '[ Эту дрянь я у тебя и задаром не возьму ]';
     STR_KEY_NOT_INTERESTED = '[ Вряд ли я смогу найти полезное применение этому ]';
     STR_H_IS_HELP = KEY_INV_HELP + ' - информация о предмете';
     STR_YOU_WEARED_TECHNO = 'Вы надели технобраслет ';
     STR_CANT_WEAR_TECHNO = 'Не могу снять ни один браслет!';
     STR_MONSTER_RELOAD = ' перезаряжает свое оружие';
     STR_YOU_WAS_KILLED_WHILE_SLEEPING = 'Вы были убиты во сне';
     STR_IN_YOUR_HANDS = 'В руках: ';
     STR_SAVESDB_ERROR = 'Ошибка в структуре файла "' + SAVESDB + '".';
     STR_INPUT_NAME = 'Имя игрока (персонаж будет мужского пола):';
     STR_INPUT_NAME_2 = 'Имя игрока: ';
     STR_ERRORIN = 'Ошибка на ';
     STR_EXPECTED = 'ожидалось ';
     STR_NAME_EXPECTED = 'ожидалась функция, переменная или процедура';
     STR_NUMBER_EXPECTED = 'ожидалось число';
     STR_END_WITHOUT_BEGIN = 'закрытие блока без его открытия';
     STR_UNDECLARED_IDENTIFIER = 'неизвестное название процедуры или функции: ';
     STR_SPECIAL_EXPECTED = 'ожидался операнд';
     STR_SRAVN_OPERAND_EXPECTED = 'ожидался операнд сравнения';
     STR_UNDECLARED_FUNCTION = 'неизвестная функция';
     STR_UNDECLARED_VARIABLE = 'неизвестная переменная';
     STR_INTEGER_EXPECTED = 'ожидалось число';
     STR_UNDECLARED_PROC = 'неизвестная процедура';
     STR_UNKNOWN_LOCATION = 'неизвестная локация';
     STR_WALL_DOWN = 'Проход открыт';
     STR_WALL_UP = 'Проход закрыт';
     STR_BRIDGE_UP = 'Мостик поднят';
     STR_BRIDGE_DOWN = 'Мостик опущен';
     STR_DOOR_OPENED = 'Дверь открыта';
     STR_DOOR_CLOSED = 'Дверь закрыта';
     STR_NOT_IN_PRIZRAK = 'Вы не в режиме разведки';
     STR_SUICIDE = 'Нажмите "' + KEY_SUICIDE + '", чтобы немедленно убить дрона и переключиться на персонажа.';
     STR_YOU_FEEL_DEATH = 'Еще одна седая прядь на голове. Вы чувствуете, что лучшие молодые годы явно остались позади...';
     STR_IT_WILL_COST = 'Установка будет стоить ';
     STR_MONETS = ' монет';
     STR_KILLED = ' и убит';
     STR_KG = ' кг';

     STR_WARRIOR_CLASS = 'Воин';
     STR_MAGE_CLASS = 'Маг';
     STR_HUNTER_CLASS = 'Охотник';
     STR_UNIQUE_CLASS = 'Уникальный';
     STR_PODCLASS_VARVAR = 'Рыцарь';
     STR_PODCLASS_SHOOTER = 'Стрелок';
     STR_PODCLASS_PSI_WAR = 'Друид';
     STR_PODCLASS_TELEPAT = 'Телепат';
     STR_PODCLASS_NECROMANT = 'Некромант';
     STR_PODCLASS_SHADOW = 'Тень';
     STR_PODCLASS_VAMPIRE = 'Вампир';
     STR_TECHNO = ' технобраслет';
     STR_PROGRAM_ACTIVE = 'Задействован нанокод';
     STR_DRINK_ACTIVE = 'Приняты нанороботы';
     STR_NOT_NEED = 'Этот предмет не нуждается в идентификации';
     STR_NANO_NOT_USE = 'Нанокод не работает здесь';
     STR_TECHNOZ: Array[1..31] of string =
     (
       'Золотой', 'Аметистовый', 'Берилловый', 'Алмазный',
       'Изумрудный', 'Гранатовый', 'Гранитовый', 'Хрустальный',
       'Малахитовый', 'Мраморный', 'Ониксовый', 'Опаловый',
       'Жемучжный', 'Кварцевый', 'Рубиновый', 'Сапфировый',
       'Железный', 'Медный', 'Платиновый', 'Стальной',
       'Флюоритовый', 'Жадовый', 'Хрустальный', 'Малахитовый',
       'Родонитовый', 'Топазовый', 'Бронзовый', 'Обсидановый',
       'Адаманитовый', 'Азуритовый', 'Аквамариновый'
     );
     TECHNOCOLORS: Array[1..31] of TColor =
     (
       Yellow, Magenta, LightGreen, White, Green, Red,
       LightGray, White, Green, LightGray, LightRed, White,
       White, White, Red, Blue, DarkGray, LightGray,
       LightGray, DarkGray, LightGreen, LightGreen, White,
       Green, LightRed, Yellow, Brown, LightGray, LightGreen,
       LightBlue, LightCyan
     );
     STR_PROG = 'Нанокод';
     STR_NANOZ: Array[1..25] of string =
     (
       'engada fa', 'allergo fiil', 'sh0ck', 'mganba aes',
       'gesto arriv', 'banoxi tabi', 'meerk kho', 'mungesh trolti',
       'dok uming', 'pet oxash', 'fabar blatrol', 'heankin',
       'st@n1n', 'eshyer syden', 'marag tishne', 'plupin cop',
       'viv wedyrg', 'asatar ees', 'sukishna li', 'karaleris',
       'uluk attenti', 'zikla mantana', 'elcatin', 'tre amutti',
       'arrazza mi'
     );
     STR_DRINK = 'Нанороботы';
     STR_DRINKZ: Array[1..45] of string =
     (
       'Синие', 'Зеленые', 'Салатовые', 'Красные',
       'Фиолетовые', 'Коричневые', 'Светло-серые',
       'Темно-серые', 'Светло-синие', 'Светло-зеленые',
       'Светло-красные', 'Светло-фиолетовые', 'Желтые',
       'Белые', 'Вишневые', 'Грязно-желтые', 'Грязно-зеленые',
       'Водянисто-голубые', 'Небесно-голубые', 'Ржавые',
       'Малиновые', 'Грязно-белые', 'Медные',
       'Кроваво-красные', 'Бледно-синие', 'Бледно-зеленые',
       'Бурые', 'Прозрачные', 'Пурпурные', 'Туманные',
       'Синеватые', 'Сиреневые', 'Серебристые',
       'Солнечно-желтые', 'Багровые', 'Темно-желтые',
       'Золотистые', 'Лазуревые', 'Темно-коричневые',
       'Нежно-зеленые', 'Светло-золотистые', 'Сине-зеленые',
       'Металлические', 'Светло-сиреневые', 'Темно-сиреневые'
     );
     DRINKCOLORS: Array[1..45] of TColor =
     (
       Blue, Green, Cyan, Red, Magenta, Brown, LightGray,
       DarkGray, LightBlue, LightGreen, LightRed, LightCyan,
       Yellow, White, Red, Yellow, Green, LightBlue,
       LightBlue, Brown, Red, LightGray, LightGray, Red,
       LightBlue, LightGreen, Brown, White, Red, LightGray,
       LightBlue, LightMagenta, LightGray, Yellow, Red,
       Yellow, Yellow, Blue, Brown, LightGreen, Yellow,
       Green, LightGray, LightMagenta, Magenta
     );
     STR_ERROR_LEVEL_FILE_NOT_FOUND = 'Файл уровня не найден!';
     STR_ERROR_LOCSLIST_FILE_NOT_FOUND = 'Файл списка локаций не найден!';
     STR_YOU_FOUND_TRAP = 'Обнаружена ловушка!';
     STR_YOU_NEUTRALIZED_TRAP = 'Ловушка обезврежена';
     STR_YOU_PASS_TRAP = 'Вы успешно миновали ловушку';
     STR_HERE_NOT_TRAP = 'Здесь нечего обезвреживать';


type
    TSpecialLocation = record
      location: string;
      level: integer;
      res: string;
      cx, cy: integer;
    end;
    TEvent = procedure (x, y: integer);
    TDeathType = (DDeath, DBloodDeath, DAllBloodDeath, DDisappear, DLopnul);
    TAIModel = (AIHuman, AIZomby, AIGrib, AIGhoul, AIWorm, AITurret, AIBomzh, AICat, AIDog, AIIdiot, AIBeggar, AISeller, AIKeyChar);
    TCoord = record
      x, y: integer;
    end;
    APoint = record
      x, y: integer;
    end;
    TItemType = (ITPrimitive, ITFire, ITShells, ITNapalm, ITBows, ITElectric, ITRockets, ITCell, ITPsi,
                 ITArmor, ITSapogi, ITPlashch, ITShapka, ITTrousers, ITGloves, ITAmulet,
                 ITNanoRobot, ITEat, ITBioImplant, ITHealth, ITAmmo, ITMoney, ITKey, ITFloppy, ITBook, ITTechno,
                 ITLitter);
    Titem = record
      number: integer;
      name: string;
      expa: integer;
      itemtype: TItemType;
      InOboyma: integer;
      IsShooting: boolean;
      ShootsCount: integer;
      AmmoType: Integer;
      Effect: integer;
      help: string;
      cost: integer;
      weight: integer;
      ItemChar: char;
      ItemColor: integer;
      Frailty: integer;    // Хрупкость
      OnPickup, OnDrop, OnWear, OnUnWear, OnAttack, OnUsing: TEvent;
      Attrz: word;
      {
        1 бит - выходить ли из меню инвентаря после применения предмета
        2 бит - можно ли кучковать предметы.
        3 бит - можно ли навешивать на предмет апгрейды
      }
    end;
    TSlot = record
      number: integer;
      count: integer;
      ammo: Integer;
      bonuses: array[1..32] of integer;
    end;
    TSlot2 = record
      item: TSlot;
      itemnum: Integer;
    end;
    TItemsArray = array[1..MAXTRADEITEMS] of TSlot;
    TImplantsArray = array[1..MAXITEMS] of TSlot2;
    dataz = record
      {Характеристики, не особо влияющие на геймплей}
      name: string[255];
      health: longint;
      max_health: longint;
      money: longint;
      level: longint;
      expa: longint;
      expa_2_next: longint;
      year: longint;
      month: byte;
      day: byte;
      hours, minutes, seconds: byte;
      bYear, bMonth, bDay: byte;
      bAlso: boolean;
      age: integer;
      location: string[255];
      hardly: boolean;
      global_x, global_y: integer;
      startx, starty: integer;
      posx, posy: integer;
      missions: array[1..MAXMISSIONS] of byte;
      lastsunhour: byte;
      newposx, newposy: integer;
      cur_osv: byte;
      lookatstreet: boolean;
      peremz: array[1..MAXPEREMZ] of byte;
      expoints: integer;
      maxweight: integer;
      Light_color: integer;
      infrared: boolean;       // Смотрит ли чар на мир сквозь инфракрасные очки
      PrisonLevel: integer;    // На каком уровне пещер находится игрок (0 - он на улице)
      LastLocation: string[255];    // Название последней локации - для выхода из подземелий
      LastSavedLevel: integer;      // Пследний уровень пещер, на котором было сохранение
      LastLocationX, LastLocationY: integer; // Координаты, на которых был игрок, когда вошел в подземелье - для портала в город
      MaxLevel: integer;            // Маскимальный уровень подземелий
      UniqueItem: integer;          // Уникальный  предмет, который чар должен найти на посленем уровне подземелий
      UniqueMonster: Integer;       // Уникальный монстр, которого чар должен встретить на оследнем уровне подземелий
      InRozhok: integer;            // Количество патронов в рожке оружия, если оно есть в руке и стрелковое
      DeathAge: Integer;            // Возраст чара, после наступления которого практически перестают лечиться болезни, заживать кровотечения и проч.

      {Перки}
      ImplantsPerk: boolean; // Можно ли самому себе ставить имплантанты
      ModernPerk: boolean;   // Можно ли самому ставить улучшения на оружие

      {Базовые характеристики}
      Strength: integer;
      Intellect: integer;
      Luck: integer;
      Stamina: Integer;
      Attention: Integer;
      Agility: Integer;
      Accuracy: Integer;
      Protect_base: integer;   // В общем влияет на всю защиту и в частности - на защиту от примитива
        Protect_fire: Integer; // Защита от огнестрела  (напрямую не увеличивается, увеличивается костюмами)
        Protect_electric: Integer; // Защита от электричества
        Protect_psi: Integer; // Защита от пси-воздействия
      Weapon: Integer;
      Resistance: Integer;

      {Проклят, болен, кровоточит и т.д.}
      Fear: Integer;
      hunger: word;
      Desease: byte;
      bloodchosen: word;
      Blind: word;  // Ослеп на сколько ходов
      Palsy: word;  // Паралич
      Poisoned: word;  // Отравлен
      Ed: word; // Оглох
      Contusioned: word; // Контужен
      Tired: word;
      Sleep: boolean;

      Blind_protection: word;
      Palsy_protection: word;
      Fear_protection: word;
      Poison_protection: word;
      Ed_protection: word;
      Contusion_protection: word;
      Gluk_protection: word;

      {Заклинания}
      Invisible: integer;
      prizrak: boolean;

      {Что перс несет на себе и т.д.}
      r_hand, l_hand, costume, legs, head, gloves, amulet: Integer;
      bracelet1, bracelet2: integer;
      char1, char2: word;
      inventory: array[1..MAXITEMS] of TSlot;

      {Вещи, которые есть у продавцов}
      trade_weapons: TItemsArray;
      trade_armor: TItemsArray;
      trade_other: TItemsArray;
      implants: TImplantsArray;
      UsedImplants: array[1..IMPLANTS_MAX - IMPLANTS_MIN + 1] of Boolean;

      {Временный мусор}
      IsInInv: boolean;
      toPortal: byte;  // Ходов до телепортации в город
      anAnnigil: boolean;  // В процессе уничтожения любого предмета из инвентаря
      InfraVision: boolean;
      LastMessages: array[1..RESOLUTION_Y - 3] of string[RESOLUTION_X];
{
  Цвета:
    1. Синий (Синий)
    2. Зеленый (Зеленый)
    3. Салатовый (Циан)
    4. Красный (Красный)
    5. Фиолетовый (Магента)
    6. Коричневый (Коричневый)
    7. Светло - серый (светло - серый)
    8. Темно - серый (темно - серый)
    9. Светло - синий (светло - синий)
    10. Светло - зеленый (светло - зеленый)
    11. Светло - красный (светло - красный)
    12. Светло - фиолетовый (светло - фиолетовый)
    13. Желтый (Желтый)
    14. Белый (Белый)
    15. Вишненвый (Красный)
    16. Грязно - желтый (Желтый)
    17. Гряно - зеленый (Зеленый)
    18. Водянисто - голубой (Светло - синий)
    19. Небесно - голубой (Светло - синий)
    20. Ржавый (Коричневый)
    21. Малиновый (Красный)
    22. Грязно - белый (Светло - серый)
    23. Медный (Светло - серый)
    24. Кроваво - красный (Красный)
    25. Бледно - синий (Светло - синий)
    26. Бледно - зеленый (Свтело - зеленый)
    27. Бурый (коричневый)
    28. Прозрачный (белый)
    29. Пурпурный (красный)
    30. Туманный (Светло - серый)
    31. Синеватый (Светло - синий)
    32. Сиреневый (Светло - магента)
    33. Серебристый (Светло - серый)
    34. Солнечно - желтый (Желтый)
    35. Багровый (красный)
    36. Темно - желтый (Желтый)
    37. Золотистый (Желтый)
    38. Лазуревый (Синий)
    39. Темно - коричневый (Коричневый)
    40. Нежно - зеленый (Светло - зеленый)
    41. Светло - золотистый (Желтый)
    42. Сине - зеленый (Зеленый)
    43. Металлический (Светло - серый)
    44. Светло - сиреневый (Светло - магента)
    45. Темно - сиреневый (Магента)

  Материалы (для технобраслетов):
    1. Золотой (Желтый)
    2. Аметистовый (Магента)
    3. Берилловый (Светло - зеленый)
    4. Алмазный (Белый)
    5. Изумрудный (Зеленый)
    6. Гранатовый (Красный)
    7. Гранитовый (Светло - серый)
    8. Хрустальный (Белый)
    9. Малазитовый (зеленый)
    10. Мраморный (Светло - серый)
    11. Ониксовый (Светло - красный)
    12. Опаловый (Белый)
    13. Жемчужный (Белый)
    14. Кварцевый (Белый)
    15. Рубиновый (Красный)
    16. Сапфировый (Синий)
    17. Железный (Темно - серый)
    18. Медный (Светло - серый)
    19. Платиновый (Светло - серый)
    20. Стальной (Темно - серый)
    21. Флюоритовый (Светло - зеленый)
    22. Жадовый (Светло - зеленый)
    23. Хрустальный (Белый)
    24. Малахитовый (зеленый)
    25. Родонитовый (Светло - красный)
    26. Топазовый (Желтый)
    27. Бронзовый (коричневый)
    28. Обсидановый (Светло - серый)
    29. Адаманитовый (Светло - зеленый)
    30. Азуритовый (Светло - синий)
    31. Аквамариновый (Светло - циан)
}
      TechnoZ: array[1..31] of
        record
          num: byte;
          examined: boolean;
        end;
      NanoZ: array[1..28] of
        record
          num: byte;
          examined: boolean;
        end;
      DrinkZ: array[1..45] of
        record
          num: byte;
          examined: boolean;
        end;
    end;
    TZakl = record
      ZakType: integer;
      ZakLong: integer;
    end;
    TMonster = record
      montype: word;
      health: integer;
      max_health: Integer;
      item: TSlot;
      point: word;
      shod: boolean;
      gold: integer;
      Zak: TZakl;
      Attrz: word;
      {
        1 бит - а не глюк ли этот монстр?
      }
    end;
    TKletka = record
      lev: char;
      color: integer;
      vid, now: char;    // Что видел или видит игрок здесь в данный момент
      levAttrz: integer; // ключи для открытия дверей, преодоления ауры, триггеры для рычагов и т.д.
      item: TSlot;
      AnObject: char;
      AnObjectColor: integer;
      ObjAttrz: integer; // координаты для телепортатора, рычагов, идентификаторы для статуй и т.д.
                         // Если ObjAttrz у двери равен 256, значит, она самозакрывающаяся
      DoorCode: integer;  // код от двери, если она закрыта
      attrz: word;
      {
        1 бит - сугубо для дверей - самозакрывающаяся ли
        2 бит - дом ли (inhouse)
        //3 бит - прикрывает ли клетку куча мусора - ПОТЕРЯЛ АКТУАЛЬНОСТЬ
        3 бит - сугубо для дверей - не сломана ли
        4 бит - есть ли кровь на клетке
        5 бит - являеися ли стена на клетке секретной дверью
        6 бит - обыскан ли труп на этой клетке - ПОТЕРЯЛ АКТУАЛЬНОСТЬ!!!
        7 бит - сугубо для дверей - открывается ли на тепло\движение
        8 бит - СУГУБО ДЛЯ ROGUE - ПОДЗЕМЕЛИЙ - Коридор ли
        9 бит - СУГУБО ДЛЯ ОКОН - нельзя ли разбить стекло руками или кинутым предметом
      }
      Light: integer; // радиус освещения
      StrIndex: word;  // Идентификатор строки, которая может выводиться как hudmessage при заходе на эту клетку
      shadow: boolean; // Затененность на уличных уровнях
      Monster: TMonster; // Есть ли монстр в данной клетке, в будущем возможна переделка и убирание, а монстров - в список
      EffectType: integer;  // Для ловушек - тип, отнимает здоровье, мгновенная смерть, упасть вниз и т.д.
      Effect: integer;      // Величина эффекта для ловушки (сколько здовровья снять и т.д.)
    end;
    TKletType = record
      TypeName: string;
      Help: string;
      c: char;
      col: integer;
      Attrz: word;
      {
        1 бит - Bold ли
        2 бит - не просматриваемая ли
        3 бит - отмечается ли на карте
        4 бит - можно ли передвинуть
        5 бит - можно ли испачкать в крови
        6 бит - потерял актуальность
        7 бит - не взрывается ли при выстреле или контакте с огнем
                (только для Bold)
      }
    end;
    level_rec = record
       map: array[1..RESOLUTION_X, 1..RESOLUTION_Y] of TKletka;
       gamer_x, gamer_y: integer;
       street: boolean;
       sleep: boolean;
       dark: boolean;
       feeling, name: string[255];
       groza: boolean;
       LastVisit: Extended;
     end;
     Tlocation = record
       locname: string;
       oldCoordX, oldCoordY: integer;
       text: string;
       locnext: string;
       coordx, coordy: integer;
     end;
     TPrisonName = record
       locname: string;
       x, y: integer;
       PrisonName: string;
     end;
     TSpecLoc = record
       locname: string;
       level: integer;
       x, y: integer;
       aType: string;
       anX, anY: integer;
     end;
     TSex = (Male, Female, Other);
     TEnemy = record
       name: string[255];
       montype: word;
       health: word;
       expa: word;
       strength: word;
       Accuracy: Integer; {Шанс попадания В %}
       character: char;
       charcolor: integer;
         protect_base: word;  // Примитив
         protect_fire: word;
         protect_electric: word;
         protect_psi: word;
       attack_koeff: Real;
       defence_koeff: Real;
       exp_koeff: real;
       gold_koeff: real;
       chance_blood: byte; {Максимальный шанс - 100}
       chance_desease: byte; {Максимальный шанс - 100}
       Intellect: TAIModel;
       Death: TDeathType;
       sex: TSex;
       Item: TSlot;
       w1Min, w1Max, w2Min, w2Max: integer; // Два промежутка номеров вещей,
                                            // которые может нести монстр.
                                            // Например, генерация случайного
                                            // оружия.
       Attrz: word;
       {
         1 бит - может ли монстр поднимать предметы
         2 бит - а не нежить ли этот монстр (Если стоит на 1, то монстр - живое создание)
         3 бит - растение ли монстр
         4 бит - а может ли монстр брызгаться кровью при жизни
         5 бит - видим ли монстр

         6 бит - может ли открывать двери
         7 бит - закрывает ли за собой двери
         8 бит - открываются ли перед ней автоматические двери
         9 бит - может ли перебираться в разбитые окна

         10 бит - выпадает ли из монстра предмет после смерти (если несет)
         11 бит - выпадает ли помле монстра золото после смерти
       }
     end;

procedure readm(var item: TItem; num: integer);
procedure iteminfo(num: integer; var info: TItem);
procedure screenshot_html;
procedure screenshot_txt;
function hasitem(item: integer): boolean;
function vokrug(x, y: integer; TypeName: string): integer;
procedure loadgame(IsExit: boolean = false);
procedure vidall;
function isletter(c: char): boolean;
procedure hudmessage(text: string; msgtype: byte; KeyRead: boolean = true; KeyBefore: boolean = false);
function getname(num: integer): string;
procedure itemz;
procedure dead(s: string);
function itemscount: byte;
procedure deczapah;
procedure mon_info(monclass: word; var enemy: TEnemy); overload;
procedure mon_info(monname: string; var enemy: TEnemy); overload;
function molchun(x, y: integer): boolean;
function streetcol: byte;
function daytime: byte;
procedure hud;
procedure dverka(x, y: shortint);
procedure saveizm;
function getmysila(monster: word): integer;
function getnpcname(i: word): string;
function addammo(num: integer; var count: integer): integer;
function gettype(t: integer): string;
function hasinv(num, count: integer): integer;
procedure deletesaves;
procedure DeleteSave;
procedure DeleteBeforeExit;
procedure delete;
procedure DeleteSaz;
procedure DeleteRandom;
procedure DeletePrison;
procedure runk(x, y: shortint);
procedure check_time;
function litter(x, y: byte): byte;
function doorli(x, y: byte): boolean;
function dveri(x, y: byte): byte;
procedure charactz;
function sten(x, y: byte): boolean;
procedure level_load;
procedure loadlevel12;
function addinv(d, count: integer): boolean;
procedure putsymbol(x, y, color: byte; symbol: char);
function IsThisAMonster(x, y: byte): boolean;
function IsDoor(x, y: byte): boolean;
function IsBold(x, y: byte): boolean;
function IsLitter(x, y: byte): boolean;
function IsInHouse(x, y: byte): boolean;
function IsBlood(x, y: byte): boolean;
procedure SetBlood(x, y: byte; condition: boolean);
function IsBoldForSun(x, y: byte): boolean;
function IsFloor(x, y: byte): boolean;
function IsSeeStopping(x, y: byte): boolean;
function MarkAtMap(x, y: byte): boolean;
function MarkAtMapObject(x, y: byte): boolean;
function GetFloorChar: char;
function IsForBlood(x, y: byte): boolean;
function IsType(x, y: byte; TypeName: string): boolean;
function IsElement(x, y: integer; TypeName: string): boolean;
function IsObject(x, y: integer; TypeName: string): boolean;
procedure SetElement(x, y: byte; TypeName: string);
function AllWeight: integer;
function IsDoorCrashed(x, y: byte): boolean;
function IsMoveable(x, y: byte): boolean;
function IsDoorSelfClosing(x, y: byte): boolean;
function IsAnObject(x, y: byte; name: string): boolean;
function LookAtStreet: boolean;
procedure GenerateNextLevel;
function GetItemAt(x, y: byte): char;
function GetItemAndColorAt(x, y: byte; var c: char): integer;
procedure SayRandomPhrase(x, y: integer);
function IsMayDropMany(x, y: byte; aType: integer; count: integer): boolean;
procedure SetObject(x, y: byte; TypeName: string);
procedure Info(s: string);
procedure Error(s: string);
procedure Abort(s: string);
function Input(s: string; MaxLen: integer = 0): string;
function GetItemType(num: integer): TItemType;
function GetItemExpa(num: integer): integer;
function InStr(d: integer): string;
function GetDate: string;
function GetTime: string;
procedure Ramka(color: byte; minus: integer = 0);
procedure Bang(x, y, radius: byte; startx, starty: byte; Strength: word);
procedure osvkletka(x, y: integer; _color: byte = white);
procedure decmonhealth(x, y: byte; value: integer);
function IsSecretDoor(x, y: byte): boolean;
function IsSecretDoorWithout(x, y: byte): boolean;
function IsTypeWithout(x, y: byte; TypeName: string): boolean;
procedure pri_hear;
procedure prihear_bez;
procedure pri_smell;
procedure prismell_bez;
procedure osvkletka_now(x, y: integer);
procedure WriteCenter(text: string);
procedure DeleteAll(ext: string; folder: string = MAPFOLDER);
procedure MoveAll(ext, fFrom, fTo: string);
procedure CopyAll(ext, fFrom, fTo: string);
procedure DeleteSaveGame;
procedure SetMonster(x, y: byte; name: string; item: word = 0; count: word = 0); overload;
procedure SetMonster(x, y: byte; montype: word; item: word = 0; count: word = 0); overload;
procedure GenerateTradeItems;
function HasWeight(num, count: integer): boolean;
procedure DrawEnemyHealth(x, y: integer);
procedure ClearEnemyHealth;
procedure BloodBang(x, y: byte);
function HasAmmoLeft: integer;
function AmmoWeight(num: integer): integer;
function GetWeight: integer;
function FemaleSklon(i: integer): string;
function PointsSklon(i: integer): string;
function AroundType(x, y: integer; aType: string): integer;
function BuySellMonetsSklon(num: integer): string;
function HoursSklon(num: integer): string;
procedure AddMessageToHistory(s: string);
function IsDoorClosedForMonster(x, y, monx, mony: integer): boolean;
function IsAutoClosed(x, y: integer): boolean;
function IsAutoOpen(x, y: integer): boolean;
function IsDoorClosed(x, y: integer): boolean;
function MonstersInView: integer;
function CanPickup(num: integer): boolean;
function IsMortal(monnum: word): boolean;
function IsBlooding(num: word): boolean;
function IsGluk(x, y: byte): boolean;
function PossiblySpecial(lev: integer; loc: string): string;
procedure LoadLevelFrom(x, y: integer; MapName: string);
function CanGrab(attrz: word): boolean;
function CanOpenDoors(attrz: word): boolean;
function CanCloseDoors(attrz: word): boolean;
function CanOpenAutoDoors(attrz: word): boolean;
function IsItem(attrz: word): boolean;
function IsGold(attrz: word): boolean;
function IsBoldWin(x, y: integer): boolean;
function aBulletColor(num: integer): byte;
function aMayXplode(num: integer): boolean;
procedure BulletWay(x1, y1: integer; var x2, y2: integer; BulletColor: byte; mayXplode: boolean; isPlayer: boolean; Accuracy, Strength: word; Weapon: integer);
procedure decammo(IsReloading: boolean = false);
function Sgn(x: integer): integer;
function GetSaveFolder: string;
function SetSaveFolder: string;
procedure DeletePlayer;
function IsDoorBroken(x, y: integer): boolean;
function IsInSkala(x, y: integer): boolean;
function GetPrisonName(locname: string; x, y: integer): string;
procedure RepaintScreen;
function GetItemByName(itemName: string): integer;
function IsExplodive(x, y: integer; aType: integer): boolean;
function GetInventoryName(num: integer): string;
procedure UseEffect(aType, aCount: integer; Stav: boolean);
procedure UseTrap(x, y: Integer);
procedure CheckForTraps(x, y: Integer);
procedure TryFoundTrap(x, y: Integer);
procedure TryNeutralizeTrap(x, y: integer);
function SexSklon(sex: TSex): string;
procedure CopyScreen(x: Integer = 1; y: integer = 1; xc: Integer = 1500; yc: Integer = 1500;
  sx: Integer = 1; sy: Integer = 1; sex: Integer = RESOLUTION_X; sey: Integer = RESOLUTION_Y + HUDSIZE - 2);
procedure PasteScreen(x: Integer = 1; y: integer = 1; xc: Integer = 1500; yc: Integer = 1500;
  sx: Integer = 1; sy: Integer = 1; sex: Integer = RESOLUTION_X; sey: Integer = RESOLUTION_Y + HUDSIZE - 2);
function Weight(num: Integer): string;
function getPlayerColor(): TColor;
function isCountable(item: Titem): boolean;
function FirstEmptySlot: Integer;
function FirstItemSlot(item: TSlot): Integer;
function isMayUpgrade(item> TItem): Boolean;


const
  Pi: double = 3.1415926535;

var
  cur_lev: level_rec;   // Текущий уровень
  data: dataz;          // Игрок
  t: text;
  xc, yc, xc1, yc1, findx, findy: byte;
  stena, door: boolean; // Нужно при освещении
  c: char;
  g1: byte;
  str12: string;
  closed: boolean;
  xc9, yc9: byte;
  levs: string;
  monstr_popal: boolean;
  txt: file of level_rec;
  DontDeleteSaves: boolean;
  DoNotLabyrints: boolean = false;
  Sinus, Cosinus: array[1..SINCOSCOUNT] of double;
  i: TImage;
  arrscreen: array[1..RESOLUTION_X, 1..RESOLUTION_Y + HUDSIZE] of char;
  arrcolors: array[1..RESOLUTION_X, 1..RESOLUTION_Y + HUDSIZE] of integer;

implementation

uses
  expa, Arc, aData, voice, inventar, svet, MyCRT, NewUnit1;

function isMayUpgrade(item: TItem): boolean;
begin
  Result := ((item.attrz and 4) > 0);
end;

function FirstEmptySlot: Integer;
var
  i: Integer;
begin
  for i := 1 to MAXITEMS do
    if (data.inventory[i].number = 0) then
    begin
      Result := i;
      break;
    end;
  Result := -1;
end;

function FirstItemSlot(item: TSlot): Integer;
begin
  for i := 1 to MAXITEMS do
    if ((data.inventory[i].number = item.number) and (data.inventory[i].ammo = item.ammo) and (data.inventory[i].bonuses = item.bonuses)) then
    begin
      Result := i;
      break;
    end;
  Result := -1;
end;

function isCountable(item: TItem): boolean;
begin
  Result := ((item.attrz and 2) > 0);
end;

function getPlayerColor(): TColor;
var
  item: TItem;
begin
  if (data.Costume <> 0) then
  begin
    iteminfo(data.Costume, item);
    Result := item.Color;
  end
  else
    Result := HERO_COLOR;
end;

function SexSklon(Sex: TSex): string;
begin
  case Sex of
    Male: Result := '';
    Female: Result := 'а';
    Other: Result := 'о';
  end;
end;

function Weigth(num: Integer): string;
var
  kgs, gs: Integer;
begin
  gs := num mod 100;
  kgs := num div 100;
  Result := IntToStr(kgs);
  if (gs <> 0) then
    Result := Result + '.' + IntToStr(gs);
end;

procedure CopyScreen(x: Integer = 1; y: integer = 1; xc: Integer = 1500; yc: Integer = 1500;
  sx: Integer = 1; sy: Integer = 1; sex: Integer = RESOLUTION_X; sey: Integer = RESOLUTION_Y + HUDSIZE - 2);
var
  k, j: integer;
begin
  if not Assigned(i) then
  begin
    i := TImage.Create(Form1);
    i.Parent := Form1;
    i.Left := -10000;
    i.Top := -10000;
    i.Width := 5000;
    i.Height := 5000;
    i.Visible := true;
  end;
  i.Canvas.CopyRect(Rect(x, y, xc, yc), Form1.Image1.Canvas, Rect(x, y, xc, yc));
  for j := sy to sey do
    for k := sx to sex do
    begin
      arrscreen[k, j] := MyCRT.screen[k, j];
      arrcolors[k, j] := MyCRT.screencolor[k, j];
    end;
end;

procedure PasteScreen(x: Integer = 1; y: integer = 1; xc: Integer = 1500; yc: Integer = 1500;
  sx: Integer = 1; sy: Integer = 1; sex: Integer = RESOLUTION_X; sey: Integer = RESOLUTION_Y + HUDSIZE - 2);
var
  k, j: integer;
begin
  if Assigned(i) then
  begin
    Form1.Image1.Canvas.CopyRect(Rect(x, y, xc, yc), i.Canvas, Rect(x, y, xc, yc));
    for j := sy to sey do
      for k := sx to sex do
      begin
        MyCRT.screen[k, j] := arrscreen[k, j];
        MyCRT.screencolor[k, j] := arrcolors[k, j];
      end;
  end;
end;

procedure TryNeutralizeTrap(x, y: integer);
var
  attention: Integer;
  IsObn: Boolean;
begin
  if ((x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y])) then
  begin
    if (IsType(x, y, 'Trap')) then
    begin
      attention := 5 - (data.vnimanie div 3);
      IsObn := (((Random(20) + 1) mod attention) = 0);
      if (IsObn) then
      begin
        hudmessage(STR_YOU_NEUTRALIZED_TRAP, MT_TEXT, true, true);
        SetObject(x, y, '[Empty]');
        cur_lev.map[x, y].EffectType := 0;
        cur_lev.map[x, y].Effect := 0;
      end
      else
      begin
        hudmessage(STR_YOU_NOT_NEUTRALIZE_TRAP, MT_ERROR, true, true);
        UseEffect(cur_lev.map[x, y].EffectType, cur_lev.map[x, y].Effect, false);
      end;
    end
    else
      hudmessage(STR_HERE_NOT_TRAP, MT_TEXT, true, true);
  end;
end;

procedure TryFoundTrap(x, y: Integer);
var
  attention: Integer;
  IsObn: Boolean;
begin
  if ((x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y]) and (cur_lev.map[x, y].EffectType <> 0) and (not IsType(x, y, 'Trap'))) then
  begin
    attention := 10 - (data.vnimanie div 3);
    IsObn := (((Random(20) + 1) mod attention) = 0);
    if (IsObn) then
    begin
      hudmessage(STR_YOU_FOUND_TRAP, MT_TEXT);
      SetObject(x, y, 'Trap');
    end;
  end;
end;

procedure CheckForTraps(x, y: Integer);
var
  attention: Integer;
  IsObn: Boolean;
  i, j: shortint;
begin
  for i := -1 to 1 do
    for j := -1 to 1 do
      if (i <> 0) or (j <> 0) then
        TryFoundTrap(x + i, y + j);
end;

function IsEffectOnce(aType: integer): boolean;
begin
  IsEffectOnce := ((aType and 65536) > 0);
end;

procedure UseEffect(aType, aCount: integer; Stav: boolean);
var
  i: integer;
  item: TItem;
  arr: array of APoint;
  aLen: Integer;
  x, y, j: Integer;
begin
  i := aType;
  if i > 65536 then
    i := i - 65536;
  case i of
    1:
    begin
      if (data.l_hand <> 0) then
        iteminfo(data.l_hand, item)
      else
        item.Effect := 0;
      data.health := data.health - aCount - Random(item.Effect);
      hudmessage(STR_YOU_FEEL_HURT, MT_ERROR);
    end;
    2:
    begin
      if (data.l_hand <> 0) then
        iteminfo(data.l_hand, item)
      else
        item.Effect := 0;
      data.health := data.health - aCount - Random(item.Effect);
      hudmessage(STR_YOU_FEEL_HURT_POISON, MT_ERROR);
      if (data.Poisoned <= 0) then
        hudmessage(STR_YOU_POISONED, MT_ERROR);
      if (data.Poison_protection > aCount div 2) then
        Inc(data.Poisoned)
      else
        inc(data.Poisoned, ((aCount div 2) - data.Poison_protection + 1));
    end;
    3:
    begin
      hudmessage(STR_YOU_TRAPPED_BLIND, MT_ERROR);
      if (data.Blind_protection + 3 > aCount) then
        inc(data.Blind, Random(3))
      else
        inc(data.Blind, aCount - data.Blind_protection + 3);
    end;
    4:
    begin
      hudmessage(STR_YOU_TRAPPED_ED, MT_ERROR);
      if (data.Ed_protection + 3 > aCount) then
        inc(data.Ed, Random(3))
      else
        inc(data.Ed, aCount - data.Ed_protection + 3);
    end;
    5:
    begin
      hudmessage(STR_YOU_TRAPPED_CONTUSIONED, MT_ERROR);
      if (data.Contusion_protection + 3 > aCount) then
        inc(data.Contusioned, Random(3))
      else
        inc(data.Contusioned, aCount - data.Contusion_protection + 3);
    end;
    6:
    begin
      SetLength(arr, 0);
      aLen := 0;
      for x := 1 to RESOLUTION_X do
        for y := 1 to RESOLUTION_Y do
        if ((not IsBold(x, y)) and (cur_lev.map[x, y].Monster.montype = 0)) then
        begin
          SetLength(arr, aLen + 1);
          arr[aLen].x := x;
          arr[aLen].y := y;
          Inc(aLen);
        end;
      if (aLen > 0) then
      begin
        hudmessage(STR_YOU_TRAPPED_TELEPORT, MT_ERROR);
        x := Random(aLen);
        data.posx := arr[x].x;
        data.posy := arr[x].y;
        SetLength(arr, 0);
        clean_all;
        osv;
      end;
    end;
    7:
    begin
      hudmessage(STR_YOU_TRAPPED_DOWN, MT_ERROR);
      x := -1;
      for i := 1 to RESOLUTION_X do
      begin
        for j := 1 to RESOLUTION_Y do
          if (IsType(i, j, 'PrisonDown')) then
          begin
            x := i;
            y := j;
            break;
          end;
        if (x <> -1) then
          break;
      end;
      GenerateAPrison(GetPrisonName(data.location, data.posx, data.posy), false, false);
      for i := 1 to RESOLUTION_X do
      begin
        for j := 1 to RESOLUTION_Y do
          if (IsType(i, j, 'PrisonUp')) then
          begin
            cur_lev.map[i, j].levAttrz := 256 * x + y;
            x := -1;
            break;
          end;
      if (x = -1) then
        break;
      end;
      aLen := 0;
      SetLength(arr, 0);
      for i := 1 to RESOLUTION_X do
        for j := 1 to RESOLUTION_Y do
          if (not IsBold(i, j) and (cur_lev.map[i, j].Monster.montype = 0) and not IsType(i, j, 'PrisonUp')) then
          begin
            SetLength(arr, aLen + 1);
            arr[aLen].x := i;
            arr[aLen].y := j;
            Inc(aLen);
          end;
      i := Random(aLen);
      data.posx := arr[i].x;
      data.posy := arr[i].y;
      SetLength(arr, 0);
      GoToXY(data.posx, data.posy);
      clean_all;
      osv;
    end;
  end;
  if IsEffectOnce(aType) then
  begin
    cur_lev.map[data.posx, data.posy].EffectType := 0;
    cur_lev.map[data.posx, data.posy].Effect := 0;
  end
  else if (Stav) then
    SetObject(data.posx, data.posy, 'Trap');
end;

procedure UseTrap(x, y: Integer);
var
  IsObn: boolean;
  attention: Integer;
begin
  attention := 10 - (data.vnimanie div 3);
  if (cur_lev.map[x, y].EffectType <> 0) then
  begin
    IsObn := (((Random(20) + 1) mod attention) = 0);
    if (not IsObn) then
      UseEffect(cur_lev.map[x, y].EffectType, cur_lev.map[x, y].Effect, true)
    else if not IsType(x, y, 'Trap') then
    begin
      hudmessage(STR_YOU_FOUND_TRAP, MT_TEXT);
      SetObject(x, y, 'Trap');
    end
    else
      hudmessage(STR_YOU_PASS_TRAP, MT_TEXT);
  end;
end;

function GetInventoryName(num: integer): string;
var
  item: TItem;
begin
  Result := '';
  iteminfo(num, item);
  if (num >= TECHNO_MIN) and (num <= TECHNO_MAX) then
  begin
    if (not data.TechnoZ[num - TECHNO_MIN + 1].examined) then
      Result := ' ' + STR_TECHNOZ[data.technoZ[num - TECHNO_MIN + 1].num];
    Result := Result + STR_TECHNO;
    if data.TechnoZ[num - TECHNO_MIN + 1].examined then
      Result := Result + item.name + ' ';
  end
  else if (num >= FLOPPY_MIN) and (num <= FLOPPY_MAX) then
  begin
    Result := STR_PROG;
    if (data.NanoZ[num - FLOPPY_MIN + 1].examined) then
      Result := Result + item.name
    else
      Result := Result + STR_NANOZ[data.NanoZ[num - FLOPPY_MIN + 1].num];
  end
  else if (num >= DRINK_MIN) and (num <= DRINK_MAX) then
  begin
    if data.DrinkZ[num - DRINK_MIN + 1].examined then
      Result := STR_DRINK + item.name
    else
      Result := STR_DRINKZ[data.DrinkZ[num - DRINK_MIN + 1].num] + ' ' + STR_DRINK;
  end
  else
    write(Item.name);
end;

procedure RepaintScreen;
begin
  Form1.Image1.Repaint;
end;

function IsExplodive(x, y: integer; aType: integer): boolean;
var
  c: char;
  col: TColor;
  i: integer;
begin
  if (aType = 0) then
  begin
    c := cur_lev.map[x, y].lev;
    col := cur_lev.map[x, y].color;
  end
  else
  begin
    c := cur_lev.map[x, y].AnObject;
    col := cur_lev.map[x, y].AnObjectColor;
  end;
  for i := 1 to TypesCount do
  begin
    if ((KletArr[i].c = c) and (KletArr[i].col = col)) then
    begin
      Result := ((KletArr[i].Attrz and 64) = 0);
      exit;
    end;
  end;
  Result := false;
end;

function GetItemByName(itemName: string): integer;
var
  i: integer;
begin
  Result := -1;
  for i := 1 to MaxItemsCount do
    if (ItemsArr[i].name = itemName) then
    begin
      Result := ItemsArr[i].number;
      exit;
    end;
end;

function GetPrisonName(locname: string; x, y: integer): string;
var
  i: integer;
begin
  Result := '';
  if (locname = 'random') then
  begin
    Result := STR_RANDOM_PRISON;
    exit;
  end;
  for i := 1 to PrisonNamesCount do
    if ((PrisonNames[i].locname = locname) and (PrisonNames[i].x = x)
        and (PrisonNames[i].y = y)) then
    begin
      Result := PrisonNames[i].PrisonName;
      exit;
    end;
end;

function IsInSkala(x, y: integer): boolean;
begin
  Result := true;
  if (IsType(x, y, 'Mountain') or IsType(x, y, 'Rock')) then
    exit
  else if (IsType(x, y, 'Floor_ground')) then
  begin
    if (IsType(x - 1, y - 1, 'Rock') or IsType(x - 1, y - 1, 'Mountain')) or
      (IsType(x - 1, y, 'Rock') or IsType(x - 1, y, 'Mountain')) or
      (IsType(x - 1, y + 1, 'Rock') or IsType(x - 1, y + 1, 'Mountain')) or
      (IsType(x, y - 1, 'Rock') or IsType(x, y - 1, 'Mountain')) or
      (IsType(x, y + 1, 'Rock') or IsType(x, y + 1, 'Mountain')) or
      (IsType(x + 1, y - 1, 'Rock') or IsType(x + 1, y - 1, 'Mountain')) or
      (IsType(x + 1, y, 'Rock') or IsType(x + 1, y, 'Mountain')) or
      (IsType(x + 1, y + 1, 'Rock') or IsType(x + 1, y + 1, 'Mountain')) then
        exit;
  end;
  Result := false;
end;

function IsDoorBroken(x, y: integer): boolean;
begin
  if (IsObject(x, y, 'Door') or IsObject(x, y, 'Door_open')) then
    Result := ((cur_lev.map[x, y].ObjAttrz and 256) > 0)
  else if (IsElement(x, y, 'Door') or IsElement(x, y, 'Door_open')) then
    Result := ((cur_lev.map[x, y].attrz and 4) > 0)
  else
    Result := false;
end;

procedure DeletePlayer;
var
  f, fout: TextFile;
  s, filename: string;
begin
  s := GetSaveFolder;
  if (s <> '') then
  begin
    DeleteAll('*.*', s);
    RemoveDir(s);
  end;
  AssignFile(f, SAVESDB);
  reset(f);
  AssignFile(fout, TEMPFOLDER + 'saves');
  rewrite(fout);
  while (not eof(f)) do
  begin
    System.readln(f, s);
    System.readln(f, filename);
    if (s <> data.name) then
    begin
      System.Writeln(fout, s);
      System.Writeln(fout, filename);
    end;
  end;
  CloseFile(f);
  CloseFile(fout);
  Erase(f);
  MoveFile(PChar(TEMPFOLDER + 'saves'), PChar(SAVESDB));
end;

function GetSaveFolder: string;
var
  f: TextFile;
  s: string;
  filename: string;
begin
  AssignFile(f, SAVESDB);
  Reset(f);
  while (not eof(f)) do
  begin
    System.ReadLn(f, s);
    if (eof(f)) then
      Abort(STR_SAVESDB_ERROR);
    System.Readln(f, filename);
    if (s = data.name) then
    begin
      CloseFile(f);
      Result := SAVEFOLDER + filename + '\';
      exit;
    end;
  end;
  CloseFile(f);
  Result := '';
  exit;
end;

function SetSaveFolder: string;
var
  f: TextFile;
  s: string;
  filename: string;
  i: integer;
begin
  AssignFile(f, SAVESDB);
  Reset(f);
  while (not eof(f)) do
  begin
    System.ReadLn(f, s);
    if (eof(f)) then
      Abort(STR_SAVESDB_ERROR);
    System.Readln(f, filename);
    if (s = data.name) then
    begin
      CloseFile(f);
      Result := SAVEFOLDER + filename + '\';
      exit;
    end;
  end;
  CloseFile(f);
  Append(f);
  System.Writeln(f, data.name);
  i := 0;
  while (i <= MAXUSERS) do
  begin
    if (not DirectoryExists(SAVEFOLDER + 'slot' + IntToStr(i))) then
      break;
    inc(i);
  end;
  if (not DirectoryExists(SAVEFOLDER + 'slot' + IntToStr(i))) then
  begin
    ForceDirectories(SAVEFOLDER + 'slot' + IntToStr(i));
    System.Writeln(f, 'slot' + IntToStr(i));
    Result := SAVEFOLDER + 'slot' + IntToStr(i) + '\';
  end;
  CloseFile(f);
end;

procedure DecCount(num: word);
begin
  dec(data.inventory[num].count);
  if data.inventory[num].count <= 0 then
    data.inventory[num].number := 0;
end;

procedure decammo(IsReloading: boolean = false);
var
  i: word;
  item: TItem;
begin
  iteminfo(data.r_hand, item);
  if (not IsReloading) then
  begin
    if (item.InOboyma > 0) then
    begin
      dec(data.InRozhok);
      exit;
    end;
  end;
  for i := 1 to MAXITEMS do
  begin
      if (item.itemtype = ITBows) and (data.inventory[i].number = AMMO_BOWS) then
      begin
        deccount(i);
        break;
      end
      else if (item.itemtype = ITFire) and (data.inventory[i].number = AMMO_FIRE) then
      begin
        deccount(i);
        break;
      end
      else if (item.itemtype = ITShells) and (data.inventory[i].number = AMMO_SHELLS) then
      begin
        deccount(i);
        break;
      end
      else if (item.itemtype = ITElectric) and (data.inventory[i].number = AMMO_ELECTRICAL) then
      begin
        deccount(i);
        break;
      end
      else if (item.itemtype = ITRockets) and (data.inventory[i].number = AMMO_ROCKETS) then
      begin
        deccount(i);
        break;
      end
      else if (item.itemtype = ITCell) and (data.inventory[i].number = AMMO_CELLCHARGE) then
      begin
        deccount(i);
        break;
      end
      else if (item.itemtype = ITPsi) and (data.inventory[i].number = AMMO_PSI) then
      begin
        deccount(i);
        break;
      end;
  end;
end;

function Sgn(x: integer): integer;
begin
  if (x = 0) then
    Sgn := 0
  else if x > 0 then
    Sgn := 1
  else
    Sgn := -1;
end;

procedure BulletWay(x1, y1: integer; var x2, y2: integer; BulletColor: byte; mayXplode: boolean; isPlayer: boolean; Accuracy, Strength: word; Weapon: integer);
var
  dx, dy, sdx, sdy, dxabs, dyabs, x, y, px, py: integer;
  IsY: boolean;
  enemy: TEnemy;
  item: TItem;
  weapon_uron, k: integer;
  s: string;
  udar: integer;
  popad: string;
const
  BULLET_SLEEP = 30;
begin
  IsY := false;
  if x2 < 1 then
    x2 := 1;
  if y2 < 1 then
    y2 := 1;
  dx := x2 - x1;
  dy := y2 - y1;
  if IsPlayer then
    decammo;
  hud;
  dxabs := abs(dx);
  dyabs := abs(dy);
  sdx := Sgn(dx);
  sdy := Sgn(dy);
  x := dyabs div 2;
  y := dxabs div 2;
  px := x1;
  py := y1;
  if (dxabs >= dyabs) then
  begin
    while true do
    begin
      y := y + dyabs;
      if (y >= dxabs) then
      begin
        y := y - dxabs;
        py := py + sdy;
        IsY := true;
      end;
      px := px + sdx;
      if not (px in [1..RESOLUTION_X]) or not (py in [1..RESOLUTION_Y]) then
        exit;
      if cur_lev.map[px, py].now <> #0 then
      begin
        if not IsY then
        begin
          PutSymbol(px, py, bulletColor, '-');
          RepaintScreen;
        end
        else
        begin
          if sdx > 0 then
          begin
            PutSymbol(px, py, bulletColor, '/');
            RepaintScreen;
          end
          else if sdx < 0 then
          begin
            PutSymbol(px, py, bulletColor, '\');
            RepaintScreen;
          end
          else
          begin
            PutSymbol(px, py, bulletColor, '|');
            RepaintScreen;
          end;
          IsY := false;
        end;
      end;
      if IsType(px, py, 'Barrel') then
      begin
        Bang(px, py, 4, x1, y1, 200);
        exit;
      end
      else if IsType(px, py, 'Window') then
      begin
        if (not IsBoldWin(px, py)) then
          SetElement(px, py, 'Window_crashed')
        else
          exit;
      end
      else if (IsThisAMonster(px, py)) and (cur_lev.map[px, py].Monster.montype < FRIEND) and (isPlayer) then
      begin
        udar := 100 - (Accuracy div 2);
        if udar > 0 then
          udar := Random(udar)
        else
          udar := 49;
        if (udar < 50) then
        begin
          mon_info(cur_lev.map[px, py].Monster.montype, enemy);
          weapon_uron := 0;
          iteminfo(Weapon, item);
          case item.itemtype of
            ITBows, ITFire, ITShells, ITRockets: weapon_uron := data.weapon_fire - enemy.protect_fire;
            ITElectric, ITCell: weapon_uron := data.weapon_electric - enemy.protect_electric;
            ITPsi: weapon_uron := data.weapon_psi - enemy.protect_psi;
          end;
          weapon_uron := Random(weapon_uron) + item.Effect;
          k := random(weapon_uron + Strength - enemy.protect_base);
          if (k <= 0) then
            k := 1;
          s := enemy.name;
          if IsGluk(px, py) then
          begin
            hudmessage(STR_GLUK_DISAPPEARED, MT_TEXT, false);
            decmonhealth(px, py, cur_lev.map[px, py].Monster.health)
          end
          else
          begin
            case enemy.sex of
              Male: s := s + STR_RANEN_NA;
              Female: s := s + STR_RANENA_NA;
              Other: s := s + STR_RANENO_NA;
            end;
            s := s + inttostr(k) + STR_POINTS2 + PointsSklon(k);
            if (mayXplode) then
              Bang(data.posx, data.posy, 3, px, py, weapon_uron)
            else
              decmonhealth(px, py, k);
            if cur_lev.map[px, py].now <> #0 then
              osvkletka(px, py);
            if (cur_lev.map[px, py].Monster.health <= 0) then
              case enemy.sex of
                Male: s := s + STR_I_UBIT;
                Female: s := s + STR_I_UBITA;
                Other: s := s + STR_I_UBITO;
              end;
            hudmessage(s, MT_TEXT);
            exit;
          end;
        end;
      end
      else if (px = data.posx) and (py = data.posy) and (not isPlayer) then
      begin
        udar := Random(100);
        if (udar > 50 + (Accuracy div 2)) then
        begin
          iteminfo(Weapon, item);
          weapon_uron := item.Effect + Strength;
          case item.itemtype of
            ITBows, ITFire, ITShells, ITRockets: weapon_uron := data.weapon_fire - Random(data.protect_fire);
            ITElectric, ITCell: weapon_uron := data.weapon_electric - Random(data.protect_electric);
            ITPsi: weapon_uron := data.weapon_psi - Random(data.protect_psi);
          end;
          weapon_uron := weapon_uron - Random(data.protect_base);
          case Random(8) of
            0:
            begin
              iteminfo(data.shapka, item);
              popad := STR_POPAD_HEAD;
            end;
            1, 2, 4:
            begin
              iteminfo(data.dosp, item);
              popad := STR_POPAD_BODY;
            end;
            3:
            begin
              iteminfo(data.gloves, item);
              weapon_uron := weapon_uron div 2;
              popad := STR_POPAD_HANDS;
            end;
            5, 6:
            begin
              iteminfo(data.trousers, item);
              weapon_uron := weapon_uron div 2;
              popad := STR_POPAD_LEGS;
            end;
            7:
            begin
              iteminfo(data.sapogi, item);
              weapon_uron := weapon_uron div 2;
              popad := STR_POPAD_LEGS;
            end;
          end;
          weapon_uron := weapon_uron - Random(item.Effect);
          if (weapon_uron <= 0) then
            hudmessage(STR_NOT_RANIL_YOU, MT_ERROR, false)
          else
          begin
            hudmessage(STR_RANILO + inttostr(weapon_uron) + STR_POINTS + ' (' + popad + ')', MT_ERROR, false);
            if (mayXplode) then
              Bang(data.posx, data.posy, 3, px, py, weapon_uron)
            else
              dec(data.health, weapon_uron);
          end;
        end;
      end
      else if (IsBold(px, py)) and (not IsType(px, py, 'Window_crashed')) then
        exit;
      Sleep(BULLET_SLEEP);
      if cur_lev.map[px, py].now <> #0 then
        osvkletka(px, py);
    end;
  end
  else
  begin
    while true do
    begin
      x := x + dxabs;
      if (x > dyabs) then
      begin
        x := x - dyabs;
        px := px + sdx;
        IsY := true;
      end;
      py := py + sdy;
      if not (px in [1..RESOLUTION_X]) or not (py in [1..RESOLUTION_Y]) then
        exit;
      if (cur_lev.map[px, py].now <> #0) then
      begin
        if not IsY then
        begin
          PutSymbol(px, py, DarkGray, '|');
          RepaintScreen;
        end
        else
        begin
          if (sdx > 0) then
          begin
            PutSymbol(px, py, DarkGray, '/');
            RepaintScreen;
          end
          else if (sdx < 0) then
          begin
            PutSymbol(px, py, DarkGray, '\');
            RepaintScreen;
          end
          else if (sdx = 0) then
          begin
            PutSymbol(px, py, DarkGray, '|');
            RepaintScreen;
          end;
          IsY := false;
        end;
      end;
      if IsType(px, py, 'Barrel') then
      begin
        Bang(px, py, 4, x1, y1, 200);
        exit;
      end
      else if IsType(px, py, 'Window') then
      begin
        if (not IsBoldWin(px, py)) then
          SetElement(px, py, 'Window_crashed')
        else
          exit;
      end
      else if (IsThisAMonster(px, py)) and (cur_lev.map[px, py].Monster.montype < FRIEND) and (isPlayer) then
      begin
        udar := 100 - (Accuracy div 2);
        if udar > 0 then
          udar := Random(udar)
        else
          udar := 99;
        if (udar < 50) then
        begin
          mon_info(cur_lev.map[px, py].Monster.montype, enemy);
          weapon_uron := 0;
          case item.itemtype of
            ITBows, ITFire, ITShells, ITRockets: weapon_uron := data.weapon_fire - enemy.protect_fire;
            ITElectric, ITCell: weapon_uron := data.weapon_electric - enemy.protect_electric;
            ITPsi: weapon_uron := data.weapon_psi - enemy.protect_psi;
          end;
          iteminfo(data.r_hand, item);
          weapon_uron := weapon_uron + item.Effect;
          k:=random(weapon_uron + data.strength - enemy.protect_base);
          s := enemy.name;
          case enemy.sex of
            Male: s := s + STR_RANEN_NA;
            Female: s := s + STR_RANENA_NA;
            Other: s := s + STR_RANENO_NA;
          end;
          s := s + inttostr(k) + STR_POINTS2 + PointsSklon(k);
          decmonhealth(px, py, k);
          if cur_lev.map[px, py].now <> #0 then
            osvkletka(px, py);
          if (cur_lev.map[px, py].Monster.health <= 0) then
            case enemy.sex of
              Male: s := s + ' ' + STR_I_UBIT;
              Female: s := s + ' ' + STR_I_UBITA;
              Other: s := s + ' ' + STR_I_UBITO;
            end;
          hudmessage(s, MT_TEXT);
          exit;
        end;
      end
      else if (px = data.posx) and (py = data.posy) and (not isPlayer) then
      begin
        udar := Random(100);
        if (udar > 50 + (Accuracy div 2)) then
        begin
          iteminfo(Weapon, item);
          weapon_uron := item.Effect + Strength;
          case item.itemtype of
            ITBows, ITFire, ITShells, ITRockets: weapon_uron := data.weapon_fire - Random(data.protect_fire);
            ITElectric, ITCell: weapon_uron := data.weapon_electric - Random(data.protect_electric);
            ITPsi: weapon_uron := data.weapon_psi - Random(data.protect_psi);
          end;
          weapon_uron := weapon_uron - Random(data.protect_base);
          case Random(8) of
            0:
            begin
              iteminfo(data.shapka, item);
              popad := STR_POPAD_HEAD;
            end;
            1, 2, 4:
            begin
              iteminfo(data.dosp, item);
              popad := STR_POPAD_BODY;
            end;
            3:
            begin
              iteminfo(data.gloves, item);
              weapon_uron := weapon_uron div 2;
              popad := STR_POPAD_HANDS;
            end;
            5, 6:
            begin
              iteminfo(data.trousers, item);
              weapon_uron := weapon_uron div 2;
              popad := STR_POPAD_LEGS;
            end;
            7:
            begin
              iteminfo(data.sapogi, item);
              weapon_uron := weapon_uron div 2;
              popad := STR_POPAD_LEGS;
            end;
          end;
          weapon_uron := weapon_uron - Random(item.Effect);
          if (weapon_uron <= 0) then
            hudmessage(STR_NOT_RANIL_YOU, MT_ERROR, false)
          else
          begin
            hudmessage(STR_RANILO + inttostr(weapon_uron) + STR_POINTS + ' (' + popad + ')', MT_ERROR, false);
            if (mayXplode) then
              Bang(data.posx, data.posy, 3, px, py, weapon_uron)
            else
              dec(data.health, weapon_uron);
          end;
        end;
      end
      else if (IsBold(px, py)) and (not IsType(px, py, 'Window_crashed')) then
        exit;
      Sleep(BULLET_SLEEP);
      if cur_lev.map[px, py].now <> #0 then
        osvkletka(px, py);
    end;
  end;
end;  

function aBulletColor(num: integer): byte;
var
  item: TItem;
begin
  aBulletColor := Black;
  iteminfo(num, item);
  case item.itemtype of
    ITFire, ITShells: Result := DarkGray;
    ITNapalm: Result := Yellow;
    ITBows: Result := LightGray;
    ITElectric: Result := LightBlue;
    ITRockets: Result := Brown;
    ITCell: Result := LightGreen;
    ITPsi: Result := Blue;
  end;
end;

function aMayXplode(num: integer): boolean;
var
  item: TItem;
begin
  iteminfo(num, item);
  Result := (item.itemtype = ITRockets);
end;

function CanGrab(attrz: word): boolean;
begin
  CanGrab := ((attrz and 256) > 0);
end;

function CanOpenDoors(attrz: word): boolean;
begin
  CanOpenDoors := ((attrz and 32) > 0);
end;

function CanCloseDoors(attrz: word): boolean;
begin
  CanCloseDoors := ((attrz and 64) > 0);
end;

function CanOpenAutoDoors(attrz: word): boolean;
begin
  CanOpenAutoDoors := ((attrz and 128) > 0);
end;

function IsItem(attrz: word): boolean;
begin
  IsItem := ((attrz and 512) > 0);
end;

function IsGold(attrz: word): boolean;
begin
  IsGold := ((attrz and 1024) > 0);
end;

function IsVisible(attrz: word): boolean;
begin
  IsVisible := ((attrz and 16) > 0);
end;

function IsBoldWin(x, y: word): boolean;
begin
  IsBoldWin := ((cur_lev.map[x, y].attrz and 256) > 0);
end;

function PossiblySpecial(lev: integer; loc: string): string;
var
  i: integer;
begin
  Result := '';
  for i := 1 to SpecialLocsArrLen do
  begin
    if (SpecialLocsArr[i].level = lev) and (SpecialLocsArr[i].location = loc) then
    begin
      Result := SpecialLocsArr[i].res;
      exit;
    end;
  end;
end;

procedure LoadLevelFrom(x, y: integer; MapName: string);
var
  i: integer;
  s: string;
begin
  cur_lev.map[x, y].levAttrz := data.posx * 256 + data.posy;
  data.posx := x;
  data.posy := y;
  DoUnCompress(MAPFOLDER + MapName + '.lev.dat', MAPFOLDER + 'prison' + inttostr(data.PrisonLevel) + '.lev');
  level_load;
  gotoxy(data.posx, data.posy);
  clean_all;
  osv;
end;

function IsGluk(x, y: byte): boolean;
begin
  IsGluk := ((cur_lev.map[x, y].Monster.Attrz and 1) > 0);
end;

function IsBlooding(num: word): boolean;
var
  enemy: TEnemy;
begin
  mon_info(num, enemy);
  IsBlooding := ((enemy.Attrz and 8) > 0);
end;

function IsMortal(monnum: word): boolean;
var
  enemy: TEnemy;
begin
  mon_info(monnum, enemy);
  IsMortal := ((enemy.Attrz and 2) > 0);
end;

function CanPickup(num: integer): boolean;
var
  enemy: TEnemy;
begin
  mon_info(num, enemy);
  CanPickup := ((enemy.Attrz and 1) > 0);
end;

function MonstersInView: integer;
var
  i, j: integer;
begin
  Result := 0;
  for j := 1 to RESOLUTION_Y do
    for i := 1 to RESOLUTION_X do
      if (IsThisAMonster(i, j)) and (cur_lev.map[i, j].now <> #0) then
        inc(Result);
end;

function IsDoorClosed(x, y: integer): boolean;
begin
  IsDoorClosed := false;
  if (cur_lev.map[x, y].levAttrz = 0) or
    ((cur_lev.map[x, y].levAttrz <> 0)
      and (hasitem(cur_lev.map[x, y].levAttrz))) then
        if (cur_lev.map[x, y].DoorCode = 0) then
          exit;
  IsDoorClosed := true;
end;

function HoursSklon(num: integer): string;
begin
  if ((num mod 100) in [10..20]) then
    HoursSklon := 'ов'
  else
    case (num mod 10) of
      1: HoursSklon := 'а';
      0, 2..9: HoursSklon := 'ов';
    end;
end;

function IsAutoClosed(x, y: integer): boolean;
begin
  IsAutoClosed := ((cur_lev.map[x, y].attrz and 1) > 0) or ((cur_lev.map[x, y].attrz and 64) > 0);
end;

function IsAutoOpen(x, y: integer): boolean;
begin
  IsAutoOpen := ((cur_lev.map[x, y].attrz and 64) > 0);
end;

function BuySellMonetsSklon(num: integer): string;
begin
  if ((num mod 100) in [10..20]) then
    BuySellMonetsSklon := ''
  else
    case (num mod 10) of
      1: BuySellMonetsSklon := 'у';
      2..4: BuySellMonetsSklon := 'ы';
      0, 5..9: BuySellMonetsSklon := '';
    end;
end;

function AroundType(x, y: integer; aType: string): integer;
begin
  AroundType := 0;
  if (not (x in [1..RESOLUTION_X])) or (not (y in [1..RESOLUTION_Y])) then
    exit;  
  if IsType(x - 1, y, aType) then
    inc(Result);
  if IsType(x, y - 1, aType) then
    inc(Result);
  if IsType(x + 1, y, aType) then
    inc(Result);
  if IsType(x, y + 1, aType) then
    inc(Result);
end;

function GetWeight: integer;
var
  i: integer;
  item: TItem;
begin
  Result := 0;
  for i := 1 to MAXITEMS do
  begin
    if data.inventory[i].number <> 0 then
    begin
      iteminfo(data.inventory[i].number, item);
      if (item.IsShooting) and (item.InOboyma > 0) then
        Result := Result + item.weight + (AmmoWeight(item.number) * data.inventory[i].count)
      else
        Result := Result + (item.weight * data.inventory[i].count)
    end;
  end;
  if (data.r_hand <> 0) then
  begin
    iteminfo(data.r_hand, item);
    inc(Result, item.weight);
    if item.IsShooting then
      inc(Result, data.InRozhok * AmmoWeight(data.r_hand));
  end;
  if (data.l_hand <> 0) then
  begin
    iteminfo(data.l_hand, item);
    inc(Result, item.weight);
  end;
  if (data.shapka <> 0) then
  begin
    iteminfo(data.shapka, item);
    inc(Result, item.weight);
  end;
  if (data.sapogi <> 0) then
  begin
    iteminfo(data.sapogi, item);
    inc(Result, item.weight);
  end;
  if (data.gloves <> 0) then
  begin
    iteminfo(data.gloves, item);
    inc(Result, item.weight);
  end;
  if (data.trousers <> 0) then
  begin
    iteminfo(data.trousers, item);
    inc(Result, item.weight);
  end;
  if (data.amulet <> 0) then
  begin
    iteminfo(data.amulet, item);
    inc(Result, item.weight);
  end;
  if (data.bracelet1 <> 0) then
  begin
    iteminfo(data.bracelet1, item);
    inc(Result, item.weight);
  end;
  if (data.bracelet2 <> 0) then
  begin
    iteminfo(data.bracelet2, item);
    inc(Result, item.weight);
  end;
end;

function FemaleSklon(i: integer): string;
begin
  case ((i mod 100) div 10) of
  0, 2..9:
    case (i mod 10) of
      1: FemaleSklon := 'а';
      2, 3, 4: FemaleSklon := 'ы';
      5, 6, 7, 8, 9, 0: FemaleSklon := '';
    end;
  1: FemaleSklon := '';
  end;
end;

function PointsSklon(i: integer): string;
begin
  case ((i mod 100) div 10) of
    0, 2..9:
      case i mod 10 of
        1: PointsSklon := 'у';
        2..4: PointsSklon := 'ы';
        5..9, 0: PointsSklon := '';
      end;
    1: PointsSklon := '';
  end;
end;

procedure DrawEnemyHealth(x, y: integer);
var
  curhealth: integer;
  enemy: TEnemy;
  zvezd: integer;
begin
  GoToXY(1, RESOLUTION_Y + 4);
  mon_info(cur_lev.map[x, y].Monster.montype, enemy);
  curhealth := cur_lev.map[x, y].Monster.health;
  zvezd := Round(curhealth / enemy.health * (RESOLUTION_X - 2));
  TextColor(White);
  write('[');
  if (curhealth < (enemy.health div 5)) then
    TextColor(Red)
  else if (curhealth < (enemy.health div 5) * 2) then
    TextColor(LightRed)
  else if (curhealth < (enemy.health div 5) * 3) then
    TextColor(Yellow)
  else
    TextColor(Green);
  for zvezd := zvezd downto 1 do
    write('*');
  TextColor(White);
  while (whereX < RESOLUTION_X) do
    write('-');
  write(']');
end;

procedure ClearEnemyHealth;
var
  x, y: integer;
begin
  if (screen[1, RESOLUTION_Y + 4] = ' ') then
    exit;
  x := WhereX;
  y := WhereY;
  GoToXY(1, RESOLUTION_Y + 4);
  while (WhereX <= RESOLUTION_X) do
    write(' ');
  GoToXY(x, y);
end;

function AmmoWeight(num: integer): integer;
var
  item: TItem;
begin
  iteminfo(num, item);
  case item.itemtype of
    ITBows: iteminfo(AMMO_BOWS, item);
    ITFire: iteminfo(AMMO_FIRE, item);
    ITShells: iteminfo(AMMO_SHELLS, item);
    ITNapalm: iteminfo(AMMO_NAPALM, item);
    ITElectric: iteminfo(AMMO_ELECTRICAL, item);
    ITRockets: iteminfo(AMMO_ROCKETS, item);
    ITCell: iteminfo(AMMO_CELLCHARGE, item);
    ITPsi: iteminfo(AMMO_PSI, item);
  end;
  AmmoWeight := item.weight;
end;

function HasWeight(num, count: integer): boolean;
var
  weight: integer;
  item: TItem;
begin
  weight := GetWeight;
  iteminfo(num, item);
  if (item.IsShooting) and (item.InOboyma > 0) then
    HasWeight := (weight + item.weight + (count * AmmoWeight(num)) <= data.maxweight)
  else
    HasWeight := not ((weight + (item.weight * count)) >= data.maxweight);
end;

procedure SetMonster(x, y: byte; montype: word; item: word = 0; count: word = 0);
var
  enemy: TEnemy;
  mon: TMonster;
begin
  mon := EmptyMonster;
  mon_info(montype, enemy);
  mon.montype := enemy.montype;
  mon.health := Random(enemy.health div 2) + (enemy.health div 2);
  mon.monchar := enemy.character;
  mon.item := item;
  mon.count := count;
  mon.point := 0;
  mon.shod := false;
  mon.Zak := EmptyZakl;
  cur_lev.map[x, y].Monster := mon;
end;

procedure SetMonster(x, y: byte; name: string; item: word = 0; count: word = 0);
var
  enemy: TEnemy;
  mon: TMonster;
begin
  mon := EmptyMonster;
  mon_info(name, enemy);
  mon.montype := enemy.montype;
  mon.health := Random(enemy.health div 2) + (enemy.health div 2);
  mon.monchar := enemy.character;
  mon.item := item;
  mon.count := count;
  mon.point := 0;
  mon.shod := false;
  mon.Zak := EmptyZakl;
  cur_lev.map[x, y].Monster := mon;
end;

procedure DeleteSaveGame;
var
  f: file;
  s: string;
begin
  s := GetSaveFolder;
  DeleteAll('*.sav*', s);
  DeleteAll('*.saz*', s);
  DeleteAll('prison*.*', s);
  DeleteAll('random.*', s);
  AssignFile(f, s + ARC_SAVEFILENAME);

  {$I-}
    Reset(f);
  {$I+}

  if IOResult = 0 then
  begin
    CloseFile(f);
    Erase(f);
  end;
end;

procedure DeleteAll(ext: string; folder: string = MAPFOLDER);
var
  Rec: TSearchRec;
  f: file;
begin
 if SysUtils.FindFirst(folder + ext, faAnyFile, Rec) = 0 then
  try
    repeat
      if ((Rec.Name <> '.') and (Rec.Name <> '') and (Rec.Name <> '..')) then
      begin
        AssignFile(f, folder + Rec.Name);
        erase(f);
      end;
    until SysUtils.FindNext(Rec) <> 0;
  finally
    SysUtils.FindClose(Rec);
  end;
end;

procedure MoveAll(ext, fFrom, fTo: string);
var
  Rec: TSearchRec;
  f: file;
begin
 if SysUtils.FindFirst(fFrom + ext, faAnyFile, Rec) = 0 then
  try
    repeat
      if Fileexists(fTo + Rec.Name) then
      begin
        AssignFile(f, fTo + Rec.Name);
        Erase(f);
      end;
      MoveFile(PChar(fFrom + Rec.name), PChar(fTo + Rec.Name));
    until SysUtils.FindNext(Rec) <> 0;
  finally
    SysUtils.FindClose(Rec);
  end;
end;

procedure CopyAll(ext, fFrom, fTo: string);
var
  Rec: TSearchRec;
  f: file;
begin
 if SysUtils.FindFirst(fFrom + ext, faAnyFile, Rec) = 0 then
  try
    repeat
      if Fileexists(fTo + Rec.Name) then
      begin
        AssignFile(f, fTo + Rec.Name);
        Erase(f);
      end;
      CopyFile(PChar(fFrom + Rec.name), PChar(fTo + Rec.Name), false);
    until SysUtils.FindNext(Rec) <> 0;
  finally
    SysUtils.FindClose(Rec);
  end;
end;

procedure WriteCenter(text: string);
begin
  gotoxy((RESOLUTION_X - Length(text)) div 2, wherey);
  write(text);
end;

procedure Ramka(color: byte; minus: integer = 0);
var
  i: byte;
begin
  for i := 2 to RESOLUTION_X - 1 do
  begin
    PutSymbol(i, 1, color, '=');
    PutSymbol(i, RESOLUTION_Y - minus + 8, color, '=');
  end;
  for i := 2 to RESOLUTION_Y - minus + 7 do
  begin
    PutSymbol(1, i, color, '|');
    PutSymbol(RESOLUTION_X, i, color, '|');
  end;
  PutSymbol(1, 1, color, '+');
  PutSymbol(1, RESOLUTION_Y - minus + 8, color, '+');
  PutSymbol(RESOLUTION_X, 1, color, '+');
  PutSymbol(RESOLUTION_X, RESOLUTION_Y - minus + 8, color, '+');
end;

function IsSecretDoor(x, y: byte): boolean;
begin
  IsSecretDoor := ((cur_lev.map[x, y].attrz and 16) > 0)
    and (cur_lev.map[x, y].ObjAttrz <= data.vnimanie);
end;

function IsSecretDoorWithout(x, y: byte): boolean;
begin
  IsSecretDoorWithout := ((cur_lev.map[x, y].attrz and 16) > 0);
end;

procedure BloodBang(x, y: byte);
var
  yy: byte;
begin
  yy := random(257);
  SetBlood(x, y, true);
  if (yy and 1) > 0 then
    SetBlood(x - 1, y - 1, true);
  if (yy and 2) > 0 then
    SetBlood(x, y - 1, true);
  if (yy and 4) > 0 then
    SetBlood(x + 1, y - 1, true);
  if (yy and 8) > 0 then
    SetBlood(x - 1, y, true);
  if (yy and 16) > 0 then
    SetBlood(x + 1, y, true);
  if (yy and 32) > 0 then
    SetBlood(x - 1, y + 1, true);
  if (yy and 64) > 0 then
    SetBlood(x, y + 1, true);
  if (yy and 128) > 0 then
    SetBlood(x + 1, y + 1, true);
end;

procedure decmonhealth(x, y: byte; value: integer);
var
  enemy: TEnemy;
  item: TItem;
begin
  mon_info(cur_lev.map[x, y].Monster.montype, enemy);
  dec(cur_lev.map[x, y].Monster.health, value);
  if (cur_lev.map[x, y].Monster.health <= 0) then
  begin
    if IsGluk(x, y) then
      enemy.Death := DDisappear;
    case enemy.Death of
      DDeath:
      begin
        if IsFloor(x, y) then
          SetObject(x, y, 'Corpse_examined');
      end;
      DBloodDeath:
      begin
        if (Random(10) + 1) mod 3 = 0 then
          BloodBang(x, y);
        if IsFloor(x, y) then
          SetObject(x, y, 'Corpse');
      end;
      DAllBloodDeath:
      begin
        BloodBang(x, y);
        if IsFloor(x, y) then
          SetObject(x, y, 'Corpse');
      end;
      DDisappear:
      begin
        if (not IsGluk(x, y)) then
          hudmessage(enemy.name + STR_DISAPPEAR_DEATH, MT_TEXT, true);
      end;
      DLopnul:
      begin
        hudmessage(enemy.name + STR_LOPNUL_DEATH, MT_TEXT, true);
        BloodBang(x, y);
      end;
    end;
    if enemy.Intellect = AITurret then
    begin
      if (cur_lev.map[x, y].Monster.item.ammo > 0) then
      begin
        iteminfo(cur_lev.map[x, y].Monster.item.number, item);
        cur_lev.map[x, y].Monster.item.number := item.AmmoType;
        cur_lev.map[x, y].Monster.item.count := cur_lev.map[x, y].Monster.item.ammo;
        cur_lev.map[x, y].Monster.item.ammo := 0;
      end
      else
        cur_lev.map[x, y].Monster.item := EmptySlot;
    end;
    if (cur_lev.map[x, y].Monster.item.number <> 0) and (IsItem(enemy.Attrz)) then
      DropItem2(x, y, cur_lev.map[x, y].Monster.item);
    if (cur_lev.map[x, y].Monster.gold > 0) and (IsGold(enemy.Attrz)) then
    begin
      cur_lev.map[x, y].Monster.item.number := MONEY_MIN + 1;
      cur_lev.map[x, y].Monster.item.count := cur_lev.map[x, y].Monster.gold;
      cur_lev.map[x, y].Monster.item.ammo := 0;
      DropItem2(x, y, cur_lev.map[x, y].Monster.item);
    end;
    inc(data.expa, enemy.expa);
    CheckForNextLevel;
  end
  else
    DrawEnemyHealth(x, y);
end;

procedure osvkletka_now(x, y: integer);
begin
  if ((x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y])) then
    cur_lev.map[x, y].now := cur_lev.map[x, y].lev;
end;

function IsInvisibleForPlayer(x, y: integer): boolean;
var
  enemy: TEnemy;
begin
  IsInvisibleForPlayer := false;
  if (data.infrared) or (data.InfraVision) then
    exit;
  mon_info(cur_lev.map[x, y].Monster.montype, enemy);
  IsInvisibleForPlayer := not IsVisible(enemy.Attrz);
end;

procedure osvkletka(x, y: integer; _color: byte = white);
var
  AColor: byte;
  ASymbol: char;
  ddd: cardinal;
  ctemp: char;
  enemy: TEnemy;
begin
  if not (x in [1..RESOLUTION_X]) or not (y in [1..RESOLUTION_Y]) then
    exit;
  if MarkAtMap(x, y) then
  begin
    if ((cur_lev.map[x, y].AnObject <> #0) and (MarkAtMapObject(x, y))) then
      cur_lev.map[x, y].vid := cur_lev.map[x, y].AnObject
    else
      cur_lev.map[x, y].vid := cur_lev.map[x, y].lev
  end
  else
    cur_lev.map[x, y].vid := GetFloorChar;
  if cur_lev.map[x, y].AnObject <> #0 then
    cur_lev.map[x, y].now := cur_lev.map[x, y].AnObject
  else
    cur_lev.map[x, y].now := cur_lev.map[x, y].lev;
  if (IsThisAMonster(x, y)) and (not IsInvisibleForPlayer(x, y)) then
  begin
    mon_info(cur_lev.map[x, y].Monster.montype, enemy);
    if _color = white then
      PutSymbol(x, y, enemy.charcolor, cur_lev.map[x, y].Monster.monchar)
    else
      PutSymbol(x, y, _color, cur_lev.map[x, y].Monster.monchar);
  end
  else if ((cur_lev.map[x, y].AnObject <> #0)) then
  begin
    if (_color = white) then
    begin
      if (IsSecretDoor(x, y)) then
        PutSymbol(x, y, yellow, cur_lev.map[x, y].AnObject)
      else if IsBlood(x, y) then
        PutSymbol(x, y, red, cur_lev.map[x, y].AnObject)
      else
        PutSymbol(x, y, cur_lev.map[x, y].AnObjectColor, cur_lev.map[x, y].AnObject);
    end
    else
      PutSymbol(x, y, _color, cur_lev.map[x, y].AnObject);
  end
  else if ((IsType(x, y, 'Land')) or (IsType(x, y, 'WallGround'))) then
    PutSymbol(x, y, cur_lev.map[x, y].color, cur_lev.map[x, y].lev)
  else if (cur_lev.map[x, y].item > 0) then
  begin
    ddd := GetItemAndColorAt(x, y, ctemp);
    ASymbol := ctemp;
    if (_color = white) then
      AColor := ddd
    else
      AColor := _color;
    PutSymbol(x, y, AColor, ASymbol);
  end
  else
  begin
    if IsBlood(x, y) then
      if (_color <> white) then
        AColor := _color
      else
        AColor := red
    else if (IsSecretDoor(x, y)) then
      if (_color <> white) then
        AColor := _color
      else
        AColor := yellow
    else if (_color = white) then
      AColor := cur_lev.map[x, y].color
    else
      AColor := _color;
    PutSymbol(x, y, AColor, cur_lev.map[x, y].lev);
  end;
  if (not IsInHouse(x, y)) then
    data.lookatstreet := true;
end;

procedure MakeVolna_ver(x, y: byte; dy: shortint; dlina: integer; color: byte; startx, starty: byte; Strength: word);
var
  cx, cy: integer;
  i: byte;
begin
  cx := x;
  cy := y;
  cy := cy - (dlina div 2);
  for i := 1 to dlina do
  begin
    if (cy in [1..RESOLUTION_y]) then
    begin
      osvkletka(cx, cy, color);
      PutSymbol(cx, cy, color, '*');
      if (IsThisAMonster(cx, cy)) or (IsType(cx, cy, 'Corpse')) or (IsType(cx, cy, 'Corpse_examined')) then
      begin
        if IsThisAMonster(cx, cy) then
          decmonhealth(cx, cy, Strength div (dlina div 2));
        if not IsThisAMonster(cx, cy) then
        begin
          cur_lev.map[cx, cy].AnObject := #0;
          cur_lev.map[cx, cy].AnObjectColor := 0;
          BloodBang(cx, cy);
        end;
      end;
      if ((cur_lev.map[cx, cy].AnObject <> #0) and (IsBold(cx, cy)) and IsExplodive(cx, cy, 1)) then
        cur_lev.map[cx, cy].AnObject := #0
      else if (IsBold(cx, cy) and IsExplodive(cx, cy, 0)) then
      begin
        cur_lev.map[cx, cy].lev := GetFloorChar;
        cur_lev.map[cx, cy].color := white;
      end
      else if (cx = startx) and (cy = starty) then
        data.health := data.health - (Strength div (dlina div 2));
    end;
    inc(cy);
  end;
end;

procedure MakeVolna_hor(x, y: byte; dy: shortint; dlina: integer; color: byte; startx, starty: byte; Strength: word);
var
  cx, cy: integer;
  i: byte;
begin
  cx := x;
  cy := y;
  cx := cx - (dlina div 2);
  for i := 1 to dlina do
  begin
    if (cx in [1..RESOLUTION_X]) then
    begin
      osvkletka(cx, cy, color);
      PutSymbol(cx, cy, color, '*');
      if (IsThisAMonster(cx, cy)) or (IsType(cx, cy, 'Corpse')) or (IsType(cx, cy, 'Corpse_examined')) then
      begin
        if IsThisAMonster(cx, cy) then
          decmonhealth(cx, cy, Strength div (dlina div 2))
        else
        begin
          cur_lev.map[cx, cy].AnObject := #0;
          cur_lev.map[cx, cy].AnObjectColor := 0;
          BloodBang(cx, cy);
        end;
      end;
      if ((cur_lev.map[cx, cy].AnObject <> #0) and (IsBold(cx, cy)) and (IsExplodive(cx, cy, 1))) then
        cur_lev.map[cx, cy].AnObject := #0
      else if (IsBold(cx, cy) and (IsExplodive(cx, cy, 0))) then
      begin
        cur_lev.map[cx, cy].lev := GetFloorChar;
        cur_lev.map[cx, cy].color := white;
      end
      else if (cx = startx) and (cy = starty) then
        data.health := data.health - (Strength div (dlina div 2));
    end;
    inc(cx);
  end;
end;

procedure Volna(x, y, rad: byte; startx, starty: byte; Strength: word);
var
  cx_l, cy_l, cx_r, cy_r, cy_u, cx_u, cx_d, cy_d: byte;
  dlina: integer;
  i: integer;
begin
  cx_l := x;
  cy_l := y;
  cx_r := x;
  cy_r := y;
  cx_u := x;
  cy_u := y;
  cx_d := x;
  cy_d := y;
  dlina := 1;
  for i := 1 to rad - 1 do
  begin
    if (i > 1) then
    begin
      dec(dlina, 2);
      cy_d := cy_d - 1;
      cy_u := cy_u + 1;
      cx_l := cx_l + 1;
      cx_r := cx_r - 1;
      MakeVolna_hor(cx_u, cy_u, -1, dlina, LightRed, startx, starty, Strength);
      MakeVolna_hor(cx_d, cy_d, 1, dlina, LightRed, startx, starty, Strength);
      MakeVolna_ver(cx_l, cy_l, -1, dlina, LightRed, startx, starty, Strength);
      MakeVolna_ver(cx_r, cy_r, 1, dlina, LightRed, startx, starty, Strength);
      inc(dlina, 2);
      cy_d := cy_d + 1;
      cy_u := cy_u - 1;
      cx_l := cx_l - 1;
      cx_r := cx_r + 1;
    end;
    MakeVolna_hor(cx_u, cy_u, -1, dlina, yellow, startx, starty, Strength);
    MakeVolna_hor(cx_d, cy_d, 1, dlina, yellow, startx, starty, Strength);
    MakeVolna_ver(cx_l, cy_l, -1, dlina, yellow, startx, starty, Strength);
    MakeVolna_ver(cx_r, cy_r, 1, dlina, yellow, startx, starty, Strength);
    cy_d := cy_d + 1;
    cy_u := cy_u - 1;
    cx_l := cx_l - 1;
    cx_r := cx_r + 1;
    dlina := dlina + 2;
    MakeVolna_hor(cx_u, cy_u, -1, dlina, LightRed, startx, starty, Strength);
    MakeVolna_hor(cx_d, cy_d, 1, dlina, LightRed, startx, starty, Strength);
    MakeVolna_ver(cx_l, cy_l, -1, dlina, LightRed, startx, starty, Strength);
    MakeVolna_ver(cx_r, cy_r, 1, dlina, LightRed, startx, starty, Strength);
    Sleep(150);
  end;
end;

procedure Bang(x, y, radius: byte; startx, starty: byte; Strength: word);
begin
  if (abs(x - startx) <= 1) and (abs(y - starty) <= 1) then
    data.health := data.health - Strength
  else if (IsThisAMonster(startx, starty)) then
    decmonhealth(startx, starty, Strength);
  Volna(x, y, radius, startx, starty, Strength);
end;

function GetDate: string;
var
  res: string;
begin
  res := res + IntToStr(data.day) + ' ';
  case data.month of
    1: res := res + STR_JANUARY;
    2: res := res + STR_FEBRUARY;
    3: res := res + STR_MARCH;
    $: res := res + STR_APRIL;
    5: res := res + STR_MAY;
    6: res := res + STR_JUNE;
    7: res := res + STR_JULE;
    8: res := res + STR_AUGUST;
    9: res := res + STR_SEPTEMBER;
    10: res := res + STR_OCTOBER;
    11: res := res + STR_NOVEMBER;
    12: res := res + STR_DECEMBER;
  end;
  res := res + IntToStr(data.year) + STR_YEARZ;
  GetDate := res;
end;

function GetTime: string;
begin
  GetTime := IntToStr(data.hours div 10) + IntToStr(data.hours mod 10) + ':' +
             IntToStr(data.minutes div 10) + IntToStr(data.minutes mod 10) + '.'
             + IntToStr(data.seconds div 10) + IntToStr(data.seconds mod 10);
end;

function InStr(d: integer): string;
begin
  InStr := inttostr(d);
end;

function GetItemExpa(num: integer): integer;
var
  item: TItem;
begin
  itemInfo(num, item);
  GetItemExpa := item.expa;
end;

function GetItemType(num: integer): TItemType;
var
  item: TItem;
begin
  GetItemType := ITMoney;
  iteminfo(num, item);
  if item.number = num then
    GetItemType := item.itemtype;
end;

procedure aMessage(s: string; color: integer; KeyRead: boolean);
var
  i, j: integer;
  x, y, w, h: integer;
  sx, sy: integer;
begin
  sx := WhereX;
  sy := WhereY;
  w := Length(s) + 4;
  h := 7;
  x := (RESOLUTION_X - w) div 2;
  y := (RESOLUTION_Y + HUDSIZE - h) div 2;
  TextColor(color);
  GoToXY(x, y);
  Write('+');
  for i := 1 to w - 2 do
    Write('=');
  Write('+');
  GoToXY(x, y + h - 1);
  Write('+');
  for i := 1 to w - 2 do
    Write('=');
  Write('+');
  for j := 1 to 5 do
  begin
    GoToXY(x, y + j);
    Write('|');
    for i := 1 to w - 2 do
      Write(' ');
    Write('|');
  end;
  GoToXY(x + 2, y + 3);
  Write(s);
  GoToXY(x - 1, y - 1);
  for i := 1 to w + 2 do
    Write(' ');
  GoToXY(x - 1, y + h);
  for i := 1 to w + 2 do
    Write(' ');
  GoToXY(x - 1, y);
  for i := 1 to h do
  begin
    GoToXY(x - 1, y + i - 1);
    Write(' ');
  end;
  GoToXY(x + w, y);
  for i := 1 to h do
  begin
    GoToXY(x + w, y + i - 1);
    Write(' ');
  end;
  RepaintScreen;
  if (KeyRead) then
    ReadKey;
  GoToXY(sx, sy);
end;

procedure Error(s: string);
begin
  aMessage(s, LightRed, true);
end;

procedure Info(s: string);
begin
  aMessage(s, White, false);
end;

procedure Abort(s: string);
begin
  Error(STR_CRITICAL_ERROR + s);
  halt;
end;

function Input(s: string; MaxLen: integer): string;
var
  aName: string;
  x, y, w, h: integer;
  i, j: integer;
begin
  w := Length(s) + 1 + MaxLen + 4;
  h := 5;
  x := (RESOLUTION_X - w) div 2;
  y := (RESOLUTION_Y + HUDSIZE - h) div 2;
  GoToXY(x, y);
  TextColor(white);
  Write('+');
  for i := 1 to w - 2 do
    Write('=');
  Write('+');
  GoToXY(x, y + h - 1);
  Write('+');
  for i := 1 to w - 2 do
    Write('=');
  Write('+');
  for i := 1 to h - 2 do
  begin
    GoToXY(x, y + i);
    Write('|');
    GoToXY(x + w - 1, y + i);
    Write('|');
  end;
  for j := 1 to 3 do
  begin
    GoToXY(x + 1, y + j);
    for i := 1 to w - 2 do
      Write(' ');
  end;
 GoToXY(x - 1, y - 1);
  for i := 1 to w + 1 do
    Write(' ');
  GoToXY(x - 1, y + h);
  for i := 1 to w + 1 do
    Write(' ');
  GoToXY(x - 1, y);
  for i := 1 to h do
  begin
    GoToXY(x - 1, y + i - 1);
    Write(' ');
  end;
  GoToXY(x + w, y);
  for i := 1 to h do
  begin
    GoToXY(x + w, y + i - 1);
    Write(' ');
  end;  
  GoToXY(x + 2, y + 2);
  Write(s + ' ');
  TextColor(DarkGray);
  ReadLn(aName, MaxLen);
  Result := aName;
end;

function IsMayDropMany(x, y: byte; aType: integer; count: integer): boolean;
begin              // Использовать только для патронов!!!
  IsMayDropMany := false;
  if not (aType < AMMO_MIN) or (aType > AMMO_MAX) then
    exit
  else if not (GetItemAt(x, y) in [#0, ' ']) and (cur_lev.map[x, y].item <> aType) then
    exit
  else if (cur_lev.map[x, y].item = aType) and ((count + cur_lev.map[x, y].ItemCount) > (maxint - 1)) then
    exit
  else
    IsMayDropMany := true;
end;

procedure SayRandomPhrase(x, y: integer);
begin
  clrscr;
  textcolor(white);
  case cur_lev.map[x, y].Monster.montype of
    60003: write(RandomPhrases[Random(MaxFarmer - MinFarmer + 1) + MinFarmer]);
    60005: write(RandomPhrases[Random(MaxBomzh - MinBomzh + 1) + MinBomzh]);
  end;
  readkey;
end;

function GetItemAt(x, y: byte): char;
var
  item: TItem;
begin
  iteminfo(cur_lev.map[x, y].item, item);
  GetItemAt := item.ItemChar;
end;

function GetItemAndColorAt(x, y: byte; var c: char): integer;  // Младший байт - символ, старший - цвет
var
  tempcolor: byte;
  tempchar: char;
  item: TItem;
begin
  tempchar := GetItemAt(x, y);
  if ((cur_lev.map[x, y].item > TECHNO_MAX) or (cur_lev.map[x, y].item < TECHNO_MIN))
    and ((cur_lev.map[x, y].item > DRINK_MAX) or (cur_lev.map[x, y].item < DRINK_MIN)) then
  begin
    iteminfo(cur_lev.map[x, y].item, item);
    tempcolor := item.ItemColor;
  end
  else if (cur_lev.map[x, y].item > DRINK_MAX) or (cur_lev.map[x, y].item < DRINK_MIN) then
    tempcolor := TECHNOCOLORS[data.technoZ[cur_lev.map[x, y].item - TECHNO_MIN + 1].num]
  else
    tempcolor := DRINKCOLORS[data.drinkZ[cur_lev.map[x, y].item - DRINK_MIN + 1].num];
  c := tempchar;
  GetItemAndColorAt := tempcolor;
end;

function CheckForOut(x, y: byte): boolean;
begin
  result := (x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y]);
end;

procedure GenerateNextLevel;
var
  t: integer;
begin
  inc(data.level);
  inc(data.expoints, (10 + (data.intellect div 5)) * ((data.intellect div 5) + 1));
  if data.expa <= 15000 then
    t := data.expa div 2
  else
    t := data.expa - 10000;
  data.expa_2_next := (t + (data.level * (1000 + (data.level * 10))) + Random(1000) + Random(100) + Random(10));
  if (data.level > 1) then
    hudmessage(STR_LEVELUP, MT_COOL);
end;

function LookAtStreet: boolean;
var
  i, j: shortint;
  i1, j1: byte;
begin
  LookAtStreet := false;
  i := wherex - data.cur_osv;
  if i < 1 then
    i := 1;
  j := wherey - data.cur_osv;
  if j < 1 then
    j := 1;
  for j1 := j to (j + (data.cur_osv * 2)) do
    for i1 := i to (i + (data.cur_osv * 2)) do
      if CheckForOut(i1, j1) then
        if (not IsInHouse(i1, j1)) and (cur_lev.map[i1, j1].now <> #0) then
        begin
          LookAtStreet := true;
          exit;
        end;
end;

function IsAnObject(x, y: byte; name: string): boolean;
var
  i: integer;
  res: boolean;
  c: char;
  col: byte;
begin
  res := false;
  IsAnObject := true;
  c := cur_lev.map[x, y].AnObject;
  col := cur_lev.map[x, y].AnObjectColor;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].c = c) and (KletArr[i].col = col) then
      res := (name = KletArr[i].TypeName);
    if res then
      exit;
  end;
  IsAnObject := false;
end;

function IsDoorSelfClosing(x, y: byte): boolean;
begin
  IsDoorSelfClosing := ((cur_lev.map[x, y].attrz and 1) > 0);
end;

function IsMoveable(x, y: byte): boolean;
var
  i: integer;
  c: char;
  col: byte;
begin
  IsMoveable := false;
  c := cur_lev.map[x, y].AnObject;
  col := cur_lev.map[x, y].AnObjectColor;
  if (c = #0) then
    exit;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].c = c) and (KletArr[i].col = col) then
    begin
      IsMoveable := ((KletArr[i].Attrz and 8) > 0);
      exit;
    end;
  end;
end;


function IsDoorCrashed(x, y: byte): boolean;
begin
  IsDoorCrashed := (cur_lev.map[x, y].ObjAttrz = 255);
end;

function AllWeight: integer;
var
  i, j: integer;
  item: TItem;
begin
  i := 0;
  for j := 1 to MAXITEMS do
  begin
    if (data.inventory[j].number > 0) then
    begin
      iteminfo(data.inventory[j].number, item);
      i := i + (data.inventory[j].count * item.weight);
    end;
    if (data.r_hand <> 0) then
    begin
      iteminfo(data.r_hand, item);
      i := i + item.weight;
    end;
    if (data.l_hand <> 0) then
    begin
      iteminfo(data.l_hand, item);
      i := i + item.weight;
    end;
    if (data.dosp <> 0) then
    begin
      iteminfo(data.dosp, item);
      i := i + item.weight;
    end;
    if (data.sapogi <> 0) then
    begin
      iteminfo(data.sapogi, item);
      i := i + item.weight;
    end;
    if (data.shapka <> 0) then
    begin
      iteminfo(data.shapka, item);
      i := i + item.weight;
    end;
    if (data.plashch<> 0) then
    begin
      iteminfo(data.plashch, item);
      i := i + item.weight;
    end;
    if (data.trousers <> 0) then
    begin
      iteminfo(data.trousers, item);
      i := i + item.weight;
    end;
    if (data.gloves <> 0) then
    begin
      iteminfo(data.gloves, item);
      i := i + item.weight;
    end;
    if (data.amulet <> 0) then
    begin
      iteminfo(data.amulet, item);
      i := i + item.weight;
    end;
    if (data.bracelet1 <> 0) then
    begin
      iteminfo(data.bracelet1, item);
      i := i + item.weight;
    end;
    if (data.bracelet2 <> 0) then
    begin
      iteminfo(data.bracelet2, item);
      i := i + item.weight;
    end;
  end;
  AllWeight := i;
end;

function IsForBlood(x, y: byte): boolean;
var
  i: integer;
  c: char;
  col: byte;
begin
  if cur_lev.map[x, y].AnObject = #0 then
  begin
    c := cur_lev.map[x, y].lev;
    col := cur_lev.map[x, y].color;
  end
  else
  begin
    c := cur_lev.map[x, y].AnObject;
    col := cur_lev.map[x, y].AnObjectColor;
  end;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].c = c) and (KletArr[i].col = col) then
    begin
      IsForBlood := ((KletArr[i].Attrz and 16) > 0);
      exit;
    end;
  end;
  IsForBlood := false;
end;

procedure putsymbol(x, y, color: byte; symbol: char);
begin
  if (x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y + 10]) then
  begin
    TextColor(color);
    WriteChrXY(x, y, symbol);
  end;
end;

function IsSeeStopping(x, y: byte): boolean;
var
  i, j: integer;
  c: char;
  color: byte;
  res: boolean;
begin
  IsSeeStopping := false;
  c := cur_lev.map[x, y].lev;
  color := cur_lev.map[x, y].color;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].c = c) and (KletArr[i].col = color) then
    begin
      res := ((KletArr[i].Attrz and 2) > 0);
      IsSeeStopping := res;
      if not res then
      begin
        c := cur_lev.map[x, y].AnObject;
        color := cur_lev.map[x, y].AnObjectColor;
        if (c = #0) then
          exit;
        for j := 1 to TypesCount do
        begin
          if (KletArr[j].c = c) and (KletArr[j].col = color) then
          begin
            IsSeeStopping := ((KletArr[j].Attrz and 2) > 0);
            exit;
          end;
        end;
      end
      else
        exit;
    end;
  end;
end;

function IsThisAMonster(x, y: byte): boolean;
begin
  IsThisAMonster := (cur_lev.map[x, y].Monster.montype > 0)
    and (cur_lev.map[x, y].Monster.health > 0);
end;

function isletter(c: char): boolean;
begin
  isletter := upcase(c) in ['A'..'Z', 'А'..'Я'];
end;

function IsElement(x, y: integer; TypeName: string): boolean;
var
  i: integer;
begin
  if (x < 1) or (x > RESOLUTION_X) or (y < 1) or (y > RESOLUTION_Y) then
  begin
    IsElement := false;
    exit;
  end;
  IsElement := true;
  for i := 1 to TypesCount do
    if (KletArr[i].TypeName = TypeName) and
    (cur_lev.map[x, y].lev = KletArr[i].c) and
    (cur_lev.map[x, y].color = KletArr[i].col) then
      exit;
  IsElement := false;
end;

function IsObject(x, y: integer; TypeName: string): boolean;
var
  i: integer;
begin
  if (x < 1) or (x > RESOLUTION_X) or (y < 1) or (y > RESOLUTION_Y) then
  begin
    IsObject := false;
    exit;
  end;
  IsObject := true;
  for i := 1 to TypesCount do
    if (KletArr[i].TypeName = TypeName) and
    (cur_lev.map[x, y].AnObject = KletArr[i].c) and
    (cur_lev.map[x, y].AnObjectColor = KletArr[i].col) then
      exit;
  IsObject := false;
end;

function IsType(x, y: byte; TypeName: string): boolean;
var
  i: integer;
  res: boolean;
  c: char;
  col: integer;
begin
  if (x < 1) or (x > RESOLUTION_X) or (y < 1) or (y > RESOLUTION_Y) then
  begin
    IsType := false;
    exit;
  end;
  IsType := true;
  if (cur_lev.map[x, y].AnObject = #0) then
  begin
    c := cur_lev.map[x, y].lev;
    col := cur_lev.map[x, y].color;
  end
  else
  begin
    c := cur_lev.map[x, y].AnObject;
    col := cur_lev.map[x, y].AnObjectColor;
  end;
  i := 1;
  while (i <= TypesCount) do
  begin
    if (KletArr[i].TypeName = TypeName) then
    begin
      res := ((c = KletArr[i].c) and (col = KletArr[i].col));
      if res then
        exit;
    end;
    inc(i);
  end;
  IsType := false;
end;

function IsTypeWithout(x, y: byte; TypeName: string): boolean;
var
  i: integer;
  res: boolean;
begin
  if (x < 1) or (x > RESOLUTION_X) or (y < 1) or (y > RESOLUTION_Y) then
  begin
    IsTypeWithout := false;
    exit;
  end;
  IsTypeWithout := true;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].TypeName = TypeName) then
    begin
      res := (cur_lev.map[x, y].lev = KletArr[i].c)
        and (cur_lev.map[x, y].color = KletArr[i].col);
      if res then
        exit;
    end;
  end;
  IsTypeWithout := false;
end;

procedure SetObject(x, y: byte; TypeName: string);
var
  i: integer;
begin
  i := 1;
  while (i <= TypesCount) do
  begin
    if (KletArr[i].TypeName = TypeName) then
    begin
      cur_lev.map[x, y].AnObject := KletArr[i].c;
      cur_lev.map[x, y].AnObjectColor := KletArr[i].col;
      exit;
    end;
    inc(i);
  end;
end;

procedure SetElement(x, y: byte; TypeName: string);
var
  i: integer;
begin
  if (not (x in [1..RESOLUTION_X])) or (not (y in [1..RESOLUTION_Y])) then
    exit;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].TypeName = TypeName) then
    begin
      cur_lev.map[x, y].lev := KletArr[i].c;
      cur_lev.map[x, y].color := KletArr[i].col;
      exit;
    end;
  end;
end;


function IsDoor(x, y: byte): boolean;
var
  i: integer;
  res: boolean;
begin
  IsDoor := true;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].TypeName = 'Door') then
    begin
      res := (KletArr[i].c = cur_lev.map[x, y].lev)
        and (KletArr[i].col = cur_lev.map[x, y].color);
      if res then
        exit;
    end;
  end;
  IsDoor := false;
end;

function IsFloor(x, y: byte): boolean;
var
  i: integer;
  res: boolean;
begin
  if (x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y]) then
  begin
    IsFloor := true;
    for i := 1 to TypesCount do
    begin
      if (KletArr[i].TypeName = 'Floor') or (KletArr[i].TypeName = 'Floor_water')
        or (KletArr[i].TypeName = 'Floor_blood') or (KletArr[i].TypeName = 'Floor_ground') or
          (KletArr[i].TypeName = 'Grass') or (KletArr[i].TypeName = 'Ground') then
      begin
        res := (KletArr[i].c = cur_lev.map[x, y].lev)
                     and (KletArr[i].col = cur_lev.map[x, y].color);
        if res then
          exit;
      end;
    end;
  end;
  IsFloor := false;
end;

function IsBold(x, y: byte): boolean;
var
  i, j :integer;
  c: char;
  col: byte;
  res: boolean;
begin
  IsBold := false;
  c := cur_lev.map[x, y].lev;
  col := cur_lev.map[x, y].color;
  for i := 1 to TypesCount do
  begin
    if (c = KletArr[i].c) and (col = KletArr[i].col) then
    begin
      res := ((KletArr[i].attrz and 1) > 0);
      IsBold := res;
      if res then
        exit
      else
      begin
        c := cur_lev.map[x, y].AnObject;
        col := cur_lev.map[x, y].AnObjectColor;
        if c = #0 then
          exit;
        for j := 1 to TypesCount do
        begin
          if (KletArr[j].c = c) and (KletArr[j].col = col) then
          begin
            IsBold := ((KletArr[j].Attrz and 1) > 0);
            exit;
          end;
        end;
      end;
    end;
  end;
end;

function IsBoldForSun(x, y: byte): boolean;
var
  i, j: integer;
  c: char;
  col: byte;
  res: boolean;
begin
  IsBoldForSun := false;
  c := cur_lev.map[x, y].lev;
  col := cur_lev.map[x, y].color;
  for i := 1 to TypesCount do
  begin
    if (c = KletArr[i].c) and (col = KletArr[i].col) then
    begin
      res := ((KletArr[i].attrz and 2) > 0);
      IsBoldForSun := res;
      if not res then
      begin
        c := cur_lev.map[x, y].AnObject;
        col := cur_lev.map[x, y].AnObjectColor;
        for j := 1 to TypesCount do
        begin
          if (c = KletArr[i].c) and (col = KletArr[i].col) then
          begin
            IsBoldForSun := ((KletArr[i].Attrz and 2) > 0);
            exit;
          end;
        end;
      end
      else
        exit;
    end;
  end;
end;

function IsInHouse(x, y: byte): boolean;
begin
  IsInHouse := ((cur_lev.map[x, y].attrz and 2) > 0);
end;

function IsLitter(x, y: byte): boolean;
begin
  IsLitter := (IsAnObject(x, y, 'Litter')) or (IsAnObject(x, y, 'Litter_land'))
                or (IsType(x, y, 'Land') or (IsType(x, y, 'WalGround')));
end;

function IsBlood(x, y: byte): boolean;
begin
  IsBlood := ((cur_lev.map[x, y].attrz and 8) > 0) and (IsForBlood(x, y));
end;

procedure SetBlood(x, y: byte; condition: boolean);
begin
  if not (x in [1..RESOLUTION_X]) or not (y in [1..RESOLUTION_Y]) then
    exit;
  if (condition) and (IsForBlood(x, y)) then
    cur_lev.map[x, y].attrz := (cur_lev.map[x, y].attrz or 8)
  else if IsBlood(x, y) then
    cur_lev.map[x, y].attrz := cur_lev.map[x, y].attrz - 8;
end;

function MarkAtMap(x, y: byte): boolean;
var
  i: integer;
  c: char;
  col: byte;
begin
  MarkAtMap := false;
  if (cur_lev.map[x, y].AnObject <> #0) then
  begin
    c := cur_lev.map[x, y].AnObject;
    col := cur_lev.map[x, y].AnObjectColor;
    for i := 1 to TypesCount do
    begin
      if (KletArr[i].c = c) and (KletArr[i].col = col) then
      begin
        MarkAtMap := ((KletArr[i].attrz and 4) > 0);
        if ((KletArr[i].attrz and 4) > 0) then
          exit
        else
          break;
      end;
    end;
  end;
  c := cur_lev.map[x, y].lev;
  col := cur_lev.map[x, y].color;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].c = c) and (KletArr[i].col = col) then
    begin
      MarkAtMap := ((KletArr[i].attrz and 4) > 0);
      exit;
    end;
  end;
end;

function MarkAtMapObject(x, y: byte): boolean;
var
  i: integer;
  c: char;
  col: byte;
begin
  MarkAtMapObject := false;
  c := cur_lev.map[x, y].AnObject;
  col := cur_lev.map[x, y].AnObjectColor;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].c = c) and (KletArr[i].col = col) then
    begin
      MarkAtMapObject := ((KletArr[i].attrz and 4) > 0);
      exit;
    end;
  end;
end;


function GetFloorChar: char;
var
  i: integer;
begin
  GetFloorChar := #0;
  for i := 1 to TypesCount do
  begin
    if (KletArr[i].TypeName = 'Floor') then
    begin
      GetFloorChar := KletArr[i].c;
      exit;
    end;
  end;
end;

procedure loadlevel12;
var
  txt: file of level_rec;
begin
  if (ArchiveExists(MAPFOLDER + data.location + SAZ_EXTENSION)) then
  begin
    if (ArchiveExists(MAPFOLDER + data.location + SAZ_EXTENSION)) then
      DoUncompress(MAPFOLDER + data.location + SAZ_EXTENSION + ARC_EXTENSION, MAPFOLDER + data.location + SAZ_EXTENSION);
    AssignFile(txt, MAPFOLDER + data.location + SAZ_EXTENSION);
    reset(txt);
    read(txt, cur_lev);
    CloseFile(txt);
    erase(txt);
  end
  else if (ArchiveExists(MAPFOLDER + data.location + SAV_EXTENSION)) then
  begin
    if (ArchiveExists(MAPFOLDER + data.location + SAV_EXTENSION)) then
      DoUncompress(MAPFOLDER + data.location + SAV_EXTENSION + ARC_EXTENSION, MAPFOLDER + data.location + SAV_EXTENSION);
    AssignFile(txt, MAPFOLDER + data.location + SAV_EXTENSION);
    reset(txt);
    read(txt, cur_lev);
    CloseFile(txt);
    erase(txt);
  end
  else if (ArchiveExists(MAPFOLDER + data.location + LEV_EXTENSION)) then
  begin
    if (ArchiveExists(MAPFOLDER + data.location + LEV_EXTENSION)) then
      DoUncompress(MAPFOLDER + data.location + LEV_EXTENSION + ARC_EXTENSION, MAPFOLDER + data.location + LEV_EXTENSION);
    AssignFile(txt, MAPFOLDER + data.location + LEV_EXTENSION);
    reset(txt);
    read(txt, cur_lev);
    CloseFile(txt);
    erase(txt);
  end
  else
    Abort(STR_ERROR_LEVEL_FILE_NOT_FOUND);
end;

function RandomWeapon: word;
var
  i: integer;
begin
  i := Random(10);
  if (data.level > 5) and (i > 7) then
    RandomWeapon := Random(SELL_SHELLS_MAX - WEAPON_SHELLS_MIN + 1) + WEAPON_SHELLS_MIN
  else if (data.level > 3) and (i > 5) then
    RandomWeapon := Random(SELL_FIRE_MAX - WEAPON_FIRE_MIN + 1) + WEAPON_FIRE_MIN
  else if ((i > 3) and (data.level > 3)) or ((data.level <= 3) and (i > 5)) then
    RandomWeapon := Random(WEAPON_BOWS_MAX - WEAPON_BOWS_MIN + 1) + WEAPON_BOWS_MIN
  else
    RandomWeapon := Random(SELL_PRIMITIVE_MAX - WEAPON_PRIMITIVE_MIN + 1) + WEAPON_PRIMITIVE_MIN;
end;

function RandomArmor: word;
begin
  RandomArmor := random(ARMOR_MAX - ARMOR_MIN + 1) + ARMOR_MIN;
end;

function RandomOther: word;
begin
  RandomOther := 0;
  case Random(2) of
    0: RandomOther := Random(EAT_MAX - EAT_MIN) + EAT_MIN;
    1: RandomOther := Random(MEDIC_MAX - MEDIC_MIN) + MEDIC_MIN;
  end;
end;

{$Warnings Off}
procedure GenerateTradeItems;
var
  i: integer;
  a, b: integer;
  temp, tcount: integer;
  minus: byte;
begin
  if (data.level < 5) then
    minus := 3
  else if (data.level < 8) then
    minus := 4
  else if (data.level < 9) then
    minus := 5
  else if (data.level < 10) then
    minus := 6
  else if (data.level < 11) then
    minus := 7
  else
    minus := 8;
  for i := 1 to MAXTRADEITEMS - minus do
  begin
    data.trade_weapons[i].number := RandomWeapon;
    if (not (data.trade_weapons[i].number in [WEAPON_PRIMITIVE_MIN..SELL_PRIMITIVE_MAX])) then
      data.trade_weapons[i].count := 0
    else
      data.trade_weapons[i].count := 1;
    data.trade_armor[i].number := RandomArmor;
    data.trade_armor[i].count := 1;
    data.trade_other[i].number := RandomOther;
    data.trade_other[i].count := 1;
  end;
  for a := 1 to MAXTRADEITEMS - minus - 1 do
    for b := 1 to MAXTRADEITEMS - minus - a do
      if (data.trade_weapons[b].number > data.trade_weapons[b + 1].number) then
      begin
        temp := data.trade_weapons[b].number;
        tcount := data.trade_weapons[b].count;
        data.trade_weapons[b] := data.trade_weapons[b + 1];
        data.trade_weapons[b + 1].number := temp;
        data.trade_weapons[b + 1].count := tcount;
      end;
  data.trade_weapons[i].number := AMMO_BOWS;
  data.trade_weapons[i].count := random(100) + 1;
  data.trade_weapons[i + 1].number := AMMO_FIRE;
  data.trade_weapons[i + 1].count := random(100) + 1;
  data.trade_weapons[i + 2].number := AMMO_SHELLS;
  data.trade_weapons[i + 2].count := (random(10 + data.level) + 1) mod 150;
  if (data.level > 4) then
  begin
    data.trade_weapons[i + 3].number := AMMO_NAPALM;
    data.trade_weapons[i + 3].count := (random(10 + data.level) + 1) mod 150;
  end;
  if (data.level > 7) then
  begin
    data.trade_weapons[i + 4].number := AMMO_ELECTRICAL;
    data.trade_weapons[i + 4].count := (random(10 + data.level) + 1) mod 150;
  end;
  if (data.level > 8) then
  begin
    data.trade_weapons[i + 5].number := AMMO_ROCKETS;
    data.trade_weapons[i + 5].count := (random(data.level) + 1) mod 150;
  end;
  if (data.level > 9) then
  begin
    data.trade_weapons[i + 6].number := AMMO_CELLCHARGE;
    data.trade_weapons[i + 6].count := (random(10 + data.level) + 1) mod 150;
  end;
  if (data.level > 10) then
  begin
    data.trade_weapons[i + 7].number := AMMO_PSI;
    data.trade_weapons[i + 7].count := (random(10 + data.level) + 1) mod 150;
  end;
  for i := i to MAXTRADEITEMS do
  begin
    data.trade_armor[i].number := RandomArmor;
    data.trade_armor[i].count := 1;
    data.trade_other[i].number := RandomOther;
    data.trade_other[i].count := 1;
  end;
  for a := 1 to MAXTRADEITEMS - 1 do
    for b := 1 to MAXTRADEITEMS - a do
    begin
      if (data.trade_armor[b].number > data.trade_armor[b + 1].number) then
      begin
        temp := data.trade_armor[b].number;
        tcount := data.trade_armor[b].count;
        data.trade_armor[b] := data.trade_armor[b + 1];
        data.trade_armor[b + 1].number := temp;
        data.trade_armor[b + 1].count := tcount;
      end;
      if (data.trade_other[b].number > data.trade_other[b + 1].number) then
      begin
        temp := data.trade_other[b].number;
        tcount := data.trade_other[b].count;
        data.trade_other[b] := data.trade_other[b + 1];
        data.trade_other[b + 1].number := temp;
        data.trade_other[b + 1].count := tcount;
      end;
    end;
end;
{$Warnings On}

procedure MakeRazn(razn: Extended);
var
  x, y: byte;
begin
  if razn > 30 then
    for y := 1 to RESOLUTION_Y do
      for x := 1 to RESOLUTION_X do
        if IsType(x, y, 'Corpse') then
          SetObject(x, y, 'Skeleton')
        else if IsType(x, y, 'Corpse_examined') then
          SetObject(x, y, 'Skeleton_examined');
end;

procedure SpecialGeneration;
begin
end;

procedure level_load;
var
  date: TDate;
  razn: Extended;
begin
  clrscr;
  loadlevel12;
  date := StrToDate(inttostr(data.day) + '.' + inttostr(data.month) + '.' + inttostr(data.year));
  razn := date - cur_lev.LastVisit;
  MakeRazn(razn);
  door := false;
  GenerateTradeItems;
  SpecialGeneration;
end;

function sten(x, y: byte): boolean;
begin
  sten := IsSeeStopping(x, y);
end;

function priN(oper: string; var data: longint; value: longint): boolean;
var
  sravn: boolean;
begin
  clrscr;
  sravn := false;
  if (oper = '>') and (data > value) then
    sravn := true
  else if (oper = '<') and (data < value) then
    sravn := true
  else if (oper = '==') and (data = value) then
    sravn := true
  else if (oper = '<=') and (data <= value) then
    sravn := true
  else if (oper = '>=') and (data >= value) then
    sravn := true
  else if (oper = '<>') and (data <> value) then
    sravn := true
  else if (oper = '=') then
  begin
    data := value;
    sravn := true
  end
  else if (oper = '+') then
  begin
    if data + value < maxlongint then
        inc(data, value)
    else
      data := maxlongint - 1;
    sravn := true;
  end
  else if (oper = '-') then
  begin
    if data - value >= 0 then
      dec(data, value)
    else
      data := 0;
    sravn := true;
  end;
  priN := sravn;
end;

function priW(oper: string; var data: word; value: longint): boolean;
var
  sravn: boolean;
begin
  sravn := false;
  if (oper = '>') and (data > value) then
    sravn := true
  else if (oper = '<') and (data < value) then
    sravn := true
  else if (oper = '==') and (data = value) then
    sravn := true
  else if (oper = '<=') and (data <= value) then
    sravn := true
  else if (oper = '>=') and (data >= value) then
    sravn := true
  else if (oper = '<>') and (data <> value) then
    sravn := true
  else if (oper = '=') then
  begin
    data := value;
    sravn := true
  end
  else if (oper = '+') then
  begin
    if data + value < 65536 then
        inc(data, value)
    else
      data := 65535;
    sravn := true;
  end
  else if (oper = '-') then
  begin
    if data - value >= 0 then
      dec(data, value)
    else
      data := 0;
    sravn := true;
  end;
  priW := sravn;
end;



function priB(oper: string; var data: byte; value: longint): boolean;
var
  sravn: boolean;
begin
  sravn := false;
  if (oper = '>') and (data > value) then
    sravn := true
  else if (oper = '<') and (data < value) then
    sravn := true
  else if (oper = '==') and (data = value) then
    sravn := true
  else if (oper = '<=') and (data <= value) then
    sravn := true
  else if (oper = '>=') and (data >= value) then
    sravn := true
  else if (oper = '<>') and (data <> value) then
    sravn := true
  else if (oper = '=') then
  begin
    data := value;
    sravn := true
  end
  else if (oper = '+') then
  begin
    if data + value < 256 then
        inc(data, value)
    else
      data := 255;
    sravn := true;
  end
  else if (oper = '-') then
  begin
    if data - value >= 0 then
      dec(data, value)
    else
      data := 0;
    sravn := true;
  end;
  priB := sravn;
end;

function readword(s: string; beg: byte; var j: byte): string;
var
  i: byte;
begin
  result := '';
  for i := beg to length(s) do
  begin
    if s[i] <> ' ' then
      result := result + s[i]
    else
      break;
  end;
  j := i;
  readword := result;
end;


procedure writecharB(s: string; var y: byte; data: word; var str2: boolean);
var
  x: byte;
begin
  if (y < 3) or (y > RESOLUTION_Y - 2) then
  begin
    x := (RESOLUTION_X div 2) + 2;
    if (y < 3) then
      y := 3
    else
      y := y - RESOLUTION_Y + 2;
    str2 := true;
  end
  else if str2 then
    x := (RESOLUTION_X div 2) + 2  
  else
    x := 3;
  gotoxy(x, y);
  write(s + ': ');
  if data <> 0 then
    write(IntToStr(data))
  else
  begin
    textcolor(lightred);
    write(STR_UNAVAILABLE);
    textcolor(white);
  end;
  inc(y);
end;

procedure writestringB(s: string; var y: byte; var str2: boolean);
var
  x: byte;
begin
  if (y < 3) or (y > RESOLUTION_Y - 2) then
  begin
    x := (RESOLUTION_X div 2) + 2;
    if (y < 3) then
      y := 3
    else
      y := y - RESOLUTION_Y + 2;
    str2 := true;
  end
  else if str2 then
    x := (RESOLUTION_X div 2) + 2
  else
    x := 3;
  gotoxy(x, y);
  write(s);
  inc(y);
end;

function GetMonth(num: integer): string;
begin
  case num of
    1: Result := STR_JANUARY;
    2: Result := STR_FEBRUARY;
    3: Result := STR_MARCH;
    4: Result := STR_APRIL;
    5: Result := STR_MAY;
    6: Result := STR_JUNE;
    7: Result := STR_JULE;
    8: Result := STR_AUGUST;
    9: Result := STR_SEPTEMBER;
    10: Result := STR_OCTOBER;
    11: Result := STR_NOVEMBER;
    12: Result := STR_DECEMBER;
  end;
end;

function AgeSklon(num: integer): string;
begin
  Result := '';
  if ((num div 100) div 10) = 1 then
    Result := STR_YEARSOLD
  else if ((num mod 10) in [2, 3, 4]) then
    Result := STR_YEARAOLD
  else if ((num mod 10) = 1) then
    Result := STR_YEAROLD
  else
    Result := STR_YEARSOLD;
end;

procedure charactz;
var
  xc11, yc11: byte;
  y: byte;
  str2: boolean;
  i, itemnum: Integer;
  item: Titem;
begin
  str2 := false;
  xc11 := wherex;
  yc11 := wherey;
  clrscr;
  Ramka(LightGray);
  gotoxy(3, 2);
  textcolor(white);
  write(data.name + ' ' + IntToStr(data.age) + AgeSklon(data.age));
  gotoxy(3, WhereY + 1);
  write(STR_BIRTHDATE);
  if ((data.day = data.bDay) and (data.month = data.bMonth)) then
    TextColor(LightGreen);
  write(IntToStr(data.bDay) + ' ' + GetMonth(data.bMonth) + IntToStr(data.bYear) + STR_YEARZ);
  TextColor(White);
  gotoxy(3, wherey + 1);
  write(STR_HEALTH + ' ');
  if data.Health <> 0 then
    write(IntToStr(data.health) + '\' + IntToStr(data.max_health));
  gotoxy(3, wherey + 1);
  write(STR_MONEY + IntToStr(data.money));
  gotoxy(3, wherey + 1);
  write(STR_WEIGHT + IntToStr(GetWeight) + '\' + IntToStr(data.maxweight));
  gotoxy(3, wherey + 1);
  write(STR_LEVEL + IntToStr(data.level));
  gotoxy(3, wherey + 1);
  gotoxy(3, wherey + 1);
  write(STR_EXPA + IntToStr(data.expa));
  gotoxy(3, wherey+1);
  write(STR_EXPA2NEXT + IntToStr(data.expa_2_next));
  gotoxy(3, wherey+1);
  y := wherey + 1;
  writecharB(STR_SKILL_STRENGTH, y, data.strength, str2);
  writecharB(STR_SKILL_INTELLECT, y, data.intellect, str2);
  writecharB(STR_SKILL_LUCK, y, data.luck, str2);
  writecharB(STR_SKILL_TRADING, y, data.trading, str2);
  writecharB(STR_SKILL_LOVKOST, y, data.lovkost, str2);
  writecharB(STR_SKILL_METKOST, y, data.metkost, str2);
  writecharB(STR_SKILL_VINOSLIVOST, y, data.vinoslivost, str2);
  writecharB(STR_SKILL_SURGERY, y, data.surgery, str2);
  writecharB(STR_SKILL_STEALTH, y, data.stealth, str2);
  writecharB(STR_SKILL_VNIMANIE, y, data.vnimanie, str2);
  writecharB(STR_SKILL_PROTECT_BASE, y, data.protect_base, str2);
  writecharB(STR_SKILL_PROTECT_FIRE, y, data.protect_fire, str2);
  writecharB(STR_SKILL_PROTECT_ELECTRIC, y, data.protect_electric, str2);
  writecharB(STR_SKILL_PROTECT_PSI, y, data.weapon_psi, str2);
  writecharB(STR_SKILL_WEAPON_PRIMITIVE, y, data.weapon_primitive, str2);
  writecharB(STR_SKILL_WEAPON_FIRE, y, data.weapon_fire, str2);
  writecharB(STR_SKILL_WEAPON_ELECTRIC, y, data.weapon_electric, str2);
  writecharB(STR_SKILL_WEAPON_PSI, y, data.weapon_psi, str2);
  writecharB(STR_BLIND_PROTECTION, y, data.Blind_protection, str2);
  writecharB(STR_PALSY_PROTECTION, y, data.Palsy_protection, str2);
  writecharB(STR_FEAR_PROTECTION, y, data.Fear_protection, str2);
  writecharB(STR_POISON_PROTECTION, y, data.Poison_protection, str2);
  writecharB(STR_GLUK_PROTECTION, y, data.Gluk_protection, str2);
  writecharB(STR_CONTUSION_PROTECTION, y, data.Contusion_protection, str2);
  Inc(y);
  if data.ustal < 100 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(red);
    write(STR_VERY_VERY_TIRED);
  end
  else if data.ustal < 1000 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(Yellow);
    write(STR_VERY_TIRED);
  end
  else if data.ustal < 10000 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightGray);
    write(STR_TIRED);
  end;
  if data.hunger < 100 then
  begin
    gotoxy(3, wherey+1);
    textcolor(red);
    write(STR_VERY_VERY_HUNGRY);
  end
  else if data.hunger < 1000 then
  begin
    gotoxy(3, wherey+1);
    textcolor(yellow);
    write(STR_VERY_HUNGRY);
  end
  else if data.hunger < 10000 then
  begin
    gotoxy(3, wherey+1);
    textcolor(LightGray);
    write(STR_HUNGRY);
  end;
  if data.bolezn > 10 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(red);
    write(STR_VERY_VERY_ILL);
  end
  else if data.bolezn > 5 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(yellow);
    write(STR_VERY_ILL);
  end
  else if data.bolezn > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightGray);
    write(STR_ILL);
  end;
  if data.bloodchosen > 10 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(red);
    write(STR_VERY_VERY_BLOODCHOSEN);
  end
  else if data.bloodchosen > 5 then
  begin
    gotoxy(3, wherey+1);
    textcolor(yellow);
    write(STR_VERY_BLOODCHOSEN);
  end
  else if data.bloodchosen > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightGray);
    write(STR_BLOODCHOSEN);
  end;
  if data.strah then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightRed);
    write(STR_SCARED);
  end;
  if data.Blind > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightRed);
    write(STR_BLIND);
  end;
  if data.Ed > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightRed);
    write(STR_ED);
  end;
  if data.Palsy > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightRed);
    write(STR_PALSIED);
  end;
  if data.Poisoned > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightRed);
    write(STR_POISONED);
  end;
  if data.Contusioned > 0 then
  begin
    gotoxy(3, wherey + 1);
    textcolor(LightRed);
    write(STR_CONTUSIONED);
  end;
  Inc(y);
  for i := 1 to IMPLANTS_MAX - IMPLANTS_MIN + 1 do
  begin
    if (data.UsedImplants[i]) then
    begin
      TextColor(Yellow);
      itemnum := IMPLANTS_MIN + i - 1;
      iteminfo(itemnum, item);
      writestringB(STR_USED_IMPLANT + item.name, y, str2);
    end;
  end;
  TextColor(White);
  readkey;
  clrscr;
  gotoxy(xc11, yc11);
end;

function dveri(x, y: byte): byte;
var
  res: byte;
begin
  res := 0;
  if (IsType(x - 1, y, 'Door_open') and (cur_lev.map[x - 1, y].levAttrz = 0) and (cur_lev.map[x - 1, y].DoorCode = 0)) then
    inc(res);
  if (IsType(x + 1, y, 'Door_open') and (cur_lev.map[x + 1, y].levAttrz = 0) and (cur_lev.map[x + 1, y].DoorCode = 0)) then
    inc(res);
  if (IsType(x, y + 1, 'Door_open') and (cur_lev.map[x, y + 1].levAttrz = 0) and (cur_lev.map[x, y + 1].DoorCode = 0)) then
    inc(res);
  if (IsType(x, y - 1, 'Door_open') and (cur_lev.map[x, y - 1].levAttrz = 0) and (cur_lev.map[x, y - 1].DoorCode = 0)) then
    inc(res);
  if (IsType(x - 1, y - 1, 'Door_open') and (cur_lev.map[x - 1, y - 1].levAttrz = 0) and (cur_lev.map[x - 1, y - 1].DoorCode = 0)) then
    inc(res);
  if (IsType(x - 1, y + 1, 'Door_open') and (cur_lev.map[x - 1, y + 1].levAttrz = 0) and (cur_lev.map[x - 1, y + 1].DoorCode = 0)) then
    inc(res);
  if (IsType(x + 1, y - 1, 'Door_open') and (cur_lev.map[x + 1, y - 1].levAttrz = 0) and (cur_lev.map[x + 1, y - 1].DoorCode = 0)) then
    inc(res);
  if (IsType(x + 1, y + 1, 'Door_open') and (cur_lev.map[x + 1, y + 1].levAttrz = 0) and (cur_lev.map[x + 1, y + 1].DoorCode = 0)) then
    inc(res);
  dveri := res;
end;

function doorli(x, y: byte): boolean;
begin
  doorli := (IsType(x, y, 'Door_open') and (cur_lev.map[x, y].levAttrz = 0) and (cur_lev.map[x, y].DoorCode = 0));
end;

function litter(x, y: byte): byte;
var
  res: byte;
begin
  res := 0;
  if IsLitter(x - 1, y) then
    inc(res);
  if IsLitter(x + 1, y) then
    inc(res);
  if IsLitter(x, y + 1) then
    inc(res);
  if IsLitter(x, y - 1) then
    inc(res);
  if IsLitter(x - 1, y - 1) then
    inc(res);
  if IsLitter(x - 1, y + 1) then
    inc(res);
  if IsLitter(x + 1, y - 1) then
    inc(res);
  if IsLitter(x + 1, y + 1) then
    inc(res);
  litter := res;
end;

procedure check_time;
var
  visokos: boolean;
  r: byte;
begin
  if ((data.year mod 4 = 0) and (data.year mod 100 <> 0)) or
     (data.year mod 400 = 0) then
       visokos := true
  else
       visokos := false;
  if data.seconds > 59 then
  begin
    inc(data.minutes, data.seconds div 60);
    data.seconds := data.seconds mod 60;
  end;
  if data.minutes > 59 then
  begin
    inc(data.hours, data.minutes div 60);
    data.minutes := data.minutes mod 60;
  end;
  if data.hours > 23 then
  begin
    if data.bolezn > 0 then
    begin
      hudmessage(STR_BOLEZN_STONGER, MT_WARNING, true);
      dec(data.health, (3 * data.bolezn * (10 - (data.vinoslivost div 5))));
    end;
    if (data.bolezn < 5) and (data.bolezn > 0) then
    begin
      r := random(3);
      if ((r = 0) and ((data.age < data.DeathAge) or (Random(MAXSTAROSTYEARS) > (data.age - data.DeathAge)))) then
      begin
        dec(data.bolezn);
        if (data.bolezn = 0) then
          hudmessage(STR_BOLEZN_AWAY, MT_INFO, true);
      end
      else if r = 2 then
        inc(data.bolezn);
    end
    else if (data.bolezn >= 5) then
    begin
      r := random(2);
      if r = 0 then
        inc(data.bolezn);
    end;
    inc(data.day, data.hours div 24);
    data.hours := data.hours mod 24;
  end;
  if ((data.month = 1) or (data.month = 3) or (data.month = 5)
    or (data.month = 7) or (data.month = 8) or (data.month = 10)
      or (data.month = 12)) and (data.day > 31) then
  begin
    inc(data.month);
    dec(data.day, 31);
  end
  else if ((data.month = 4) or (data.month = 6) or (data.month = 9)
    or (data.month = 11)) and (data.day > 30) then
  begin
    inc(data.month);
    dec(data.day, 30);
  end
  else if (data.month = 2) and (visokos) and (data.day > 29) then
  begin
    inc(data.month);
    dec(data.day, 29);
  end
  else if (data.month = 2) and (not visokos) and (data.day > 28) then
  begin
    inc(data.month);
    dec(data.day, 28);
  end;
  if data.month > 12 then
  begin
    inc(data.year, data.month div 12);
    data.month := data.month mod 12;
  end;
  if ((data.month = data.bMonth) and (data.day = data.bDay)and (not data.bAlso)) then
  begin
    inc(data.age);
    data.bAlso := true;
    if (data.age >= data.DeathAge) then
    begin
      hudmessage(STR_YOU_FEEL_DEATH, MT_TEXT);
      if (data.max_health > (data.age - data.DeathAge)) then
        Dec(data.max_health, (data.age - data.DeathAge))
      else if (data.age <= data.DeathAge + MAXSTAROSTYEARS) then
        data.max_health := 1
      else
        dead(STR_UMER_STAROST);
      if (data.health > data.max_health) then
        data.health := data.max_health;
    end;
  end
  else if ((data.month <> data.bMonth) or (data.day <> data.bDay)) then
    data.bAlso := false;
end;

procedure runk(x, y: shortint);
var
  tired: integer;
begin
  cur_lev.map[data.posx + x, data.posy + y].levAttrz := ((cur_lev.map[data.posx + x, data.posy + y].levAttrz) and (maxlongint - 4));
  tired := 40 - ((data.strength div 2) - data.vinoslivost);
  if (tired < 1) then
    tired := 1;
  if (IsAnObject(data.posx + x, data.posy + y, 'Litter')) or (IsAnObject(data.posx + x, data.posy + y, 'Litter_land')) then
  begin
    Cur_lev.map[data.posx + x, data.posy + y].AnObject := #0;
    dec(data.ustal, tired * 3);
    inc(data.seconds, 100 + Random(100) + (tired * 10));
    check_time;
    hudmessage(STR_RASKOPAL, MT_TEXT, false, true);
  end
  else if (IsType(data.posx + x, data.posy + y, 'Land') or IsType(data.posx + x, data.posy + y, 'WallGround')) then
  begin
    SetElement(data.posx + x, data.posy + y, 'Floor_ground');
    dec(data.ustal, tired * 3);
    inc(data.seconds, 100 + Random(100) + (tired * 10));
    check_time;
    hudmessage(STR_RASKOPAL, MT_TEXT, false, true);
  end
  else
    hudmessage(STR_NOTHING_TO_DIG, MT_TEXT, false, true);
end;

function DeleteLevelIfExists(name: string): boolean;
var
  f: file;
begin
  DeleteLevelIfExists := false;
  AssignFile(f, name + LEV_EXTENSION + ARC_EXTENSION);

  {$I-}
    Reset(f);
  {$I+}

  if (IOResult = 0) then
  begin
    DeleteLevelIfExists := true;
    CloseFile(f);
    Erase(f);
    AssignFile(f, name + SAV_EXTENSION + ARC_EXTENSION);

    {$I-}
      Reset(f);
    {$I+}

    if (IOResult = 0) then
    begin
      CloseFile(f);
      Erase(f);
    end;
    AssignFile(f, name + SAZ_EXTENSION + ARC_EXTENSION);

    {$I-}
      Reset(f);
    {$I+}

    if (IOResult = 0) then
    begin
      CloseFile(f);
      Erase(f);
    end;
  end;
end;

procedure delete;
begin
  DeleteAll('*.saz.dat');
  DeleteAll('prison*.*');
  DeleteAll('random.*');
end;

procedure DeleteSaz;
begin
  DeleteAll('*.saz.dat');
end;

procedure DeleteRandom;
begin
  DeleteAll('random.*');
end;

procedure DeletePrison;
begin
  DeleteAll('prison*.*.dat');
end;

procedure deletesaves;
begin
  DeleteAll('*.sav.dat');
end;

procedure DeleteSave;
var
  f: file;
begin
  AssignFile(f, GetSaveFolder + 'save.dat');
  if Fileexists(GetSaveFolder + 'save.dat') then
    erase(f);
end;

procedure DeleteBeforeExit;
begin
  DeleteSaz;
  if data.PrisonLevel = 0 then
    DeletePrison;
  if data.location <> 'random' then
    DeleteRandom;
end;

function ChooseHTMLColor(c, pred: byte; predfont: boolean): string;
var
  res: string;
begin
  res := '';
  if (c = pred) then
  begin
    ChooseHTMLColor := '';
    exit;
  end;
  case c of
    Black: res := '#000000';
    Blue: res := '#1205AB';
    Green: res := '#008000';
    Cyan: res := '#00F2F2';
    Red: res := '#D10111';
    Magenta: res := '#8080FF';
    Brown: res := '#804000';
    LightGray: res := '#C0C0C0';
    DarkGray: res := '#808080';
    LightBlue: res := '#0000FF';
    LightGreen: res := '#00FF00';
    LightCyan: res := '#78BCBC';
    LightRed: res := '#FF0000';
    LightMagenta: res := '#FF0080';
    Yellow: res := '#FFFF00';
    White: res := '#FFFFFF';
  end;
  res := '<font color="' + res + '">';
  if predfont then
    res := '</font>' + res;
  ChooseHTMLColor := res;
end;

function AlsoYest(x, y: byte): boolean;
var
  i: byte;
begin
  AlsoYest := true;
  for i := x to RESOLUTION_X do
    if cur_lev.map[i, y].vid <> #0 then
      exit;
  AlsoYest := false;
end;


function ChooseKletka(cx, cy: byte; var predcol: byte): string;
var
  predfont: boolean;
  res: string;
begin
  res := '';
  predfont := not ((cx = 1) and (cy = 1));
  res := ChooseHTMLColor(screencolor[cx, cy], predcol, predfont);
  if (screen[cx, cy] <> ' ') then
    res := res + screen[cx, cy]
  else
    res := res + '&nbsp;';
  predcol := screencolor[cx, cy];
  ChooseKletka := res;
end;


procedure screenshot_html;
var
  fail: text;
  cx, cy: byte;
  predcol: byte;
begin
   assignfile(fail, 'Screen ' + inttostr(data.day) + '.'
               + inttostr(data.month) + '.' + inttostr(data.year) +
                ' ' + inttostr(data.hours) + '_' + inttostr(data.minutes) +
                 '_' + inttostr(data.seconds) + '.html');
   rewrite(fail);
   System.writeln(fail, '<HTML>');
   System.writeln(fail, '  <head>');
   System.writeln(fail, '    <title>');
   System.write(fail, STR_GAME_SCREENSHOT);
    System.write(fail, data.day, ' ');
    case data.month of
      1: System.write(fail, STR_JANUARY);
      2: System.write(fail, STR_FEBRUARY);
      3: System.write(fail, STR_MARCH);
      4: System.write(fail, STR_APRIL);
      5: System.write(fail, STR_MAY);
      6: System.write(fail, STR_JUNE);
      7: System.write(fail, STR_JULE);
      8: System.write(fail, STR_AUGUST);
      9: System.write(fail, STR_SEPTEMBER);
      10: System.write(fail, STR_OCTOBER);
      11: System.write(fail, STR_NOVEMBER);
      12: System.write(fail, STR_DECEMBER);
    end;
    System.write(fail, data.year, '  ', data.hours div 10, data.hours mod 10, ':', data.minutes div 10,
    data.minutes mod 10, '.', data.seconds div 10, data.seconds mod 10, ')');
   System.writeln(fail, '    </title>');
   System.writeln(fail, '  </head>');
   System.writeln(fail, '  <body bgcolor="black"><font name="Lucida Console" size="3">');
   System.writeln(fail, '<TT>');
   predcol := 0;
   for cy := 1 to RESOLUTION_Y + HUDSIZE do
   begin
     for cx := 1 to RESOLUTION_X do
       System.write(fail, ChooseKletka(cx, cy, predcol));
     System.writeln(fail, '<br>');
   end;
   System.writeln(fail, '</TT>');
   System.writeln(fail, '  </font></body>');
   System.writeln(fail, '</HTML>');
   closefile(fail);
end;

procedure screenshot_txt;
var
  fail: textfile;
  cx, cy: byte;
begin
  assignfile(fail, 'Screen ' + inttostr(data.day) + '.'
               + inttostr(data.month) + '.' + inttostr(data.year) +
                ' ' + inttostr(data.hours) + '_' + inttostr(data.minutes) +
                 '_' + inttostr(data.seconds) + '.txt');
  rewrite(fail);
  System.write(fail, STR_GAME_SCREENSHOT);
  System.write(fail, data.day, ' ');
  case data.month of
    1: System.write(fail, STR_JANUARY);
    2: System.write(fail, STR_FEBRUARY);
    3: System.write(fail, STR_MARCH);
    4: System.write(fail, STR_APRIL);
    5: System.write(fail, STR_MAY);
    6: System.write(fail, STR_JUNE);
    7: System.write(fail, STR_JULE);
    8: System.write(fail, STR_AUGUST);
    9: System.write(fail, STR_SEPTEMBER);
    10: System.write(fail, STR_OCTOBER);
    11: System.write(fail, STR_NOVEMBER);
    12: System.write(fail, STR_DECEMBER);
  end;
  System.writeln(fail, data.year, '  ', data.hours div 10, data.hours mod 10, ':', data.minutes div 10,
    data.minutes mod 10, '.', data.seconds div 10, data.seconds mod 10, ')');
  System.Writeln(fail);
  for cy := 1 to RESOLUTION_Y + HUDSIZE do
  begin
    for cx := 1 to RESOLUTION_X do
    begin
      c := screen[cx, cy];
      if (c <> #0) then
        System.write(fail, c)
      else
        System.write(fail, ' ');
    end;
    System.writeln(fail);
  end;
  closefile(fail);
end;

function hasinv(num: TSlot; count: integer): integer;
var
  f: integer;
  item: TItem;
  j: integer;
  wght: Integer;
begin
  Result := 0;
  j := count;
  iteminfo(num.number, item);
  wght := item.weight;
  if (num.ammo > 0) then
  begin
    iteminfo(item.AmmoType, item);
    inc(wght, item.weight * num.ammo);
  end;
  wght := wght * count;
  if (GetWeight + wght > data.maxweight) then
    Result := -2
  else
  begin
    iteminfo(num.number, item);
    j := -1;
    if (isCountable(item)) then
    begin
      j := FirstItemSlot(num);
      if (j = -1) then
        j := FirstEmptySlot;
    end
    else
      j := FirstEmptySlot;
    if (j = -1) then
      Result := -1;
  end;
end;


function GetTypeString(itemtype: TItemType): string;
begin
  case itemtype of
    ITPrimitive, ITBows, ITFire, ITElectric, ITPsi: result := 'Weapon';
    ITArmor, ITSapogi, ITPlashch, ITShapka, ITGloves: result := 'Armor';
    ITAmmo: result := 'Ammo';
    ITBioImplant: result := 'Implant';
    ITNanoRobot, ITFloppy, ITBook, ITHealth: result := 'Other';
  end;
end;


function gettype(t: integer): string;
var
  item: TItem;
begin
  gettype := '';
  iteminfo(t, item);
  if item.number = t then
    gettype := GetTypeString(item.itemtype);
end;

function addinv(d, count: integer): boolean;
var
  f: integer;
  res: boolean;
  item: TItem;
begin
  res := true;
  for f := 1 to MAXITEMS do
    if (data.inventory[f].number = 0) then
      break;
  iteminfo(d, item);
  if (item.IsShooting) and (item.InOboyma > 0) then
  begin
    data.inventory[f].number := d;
    data.inventory[f].count := count;
    addinv := true;
    exit;
  end;
  if (f = MAXITEMS) and (data.inventory[f].number <> 0) and (lowercase(gettype(d)) = 'ammo') then
    addammo(0, count)
  else if lowercase(gettype(d)) = 'ammo' then
    addammo(f, count);
  if (f = MAXITEMS) and (data.inventory[f].number <> 0) then
    res := false
  else if data.inventory[f].number = 0 then
  begin
    data.inventory[f].number := d;
    data.inventory[f].count := 1;
  end;
  addinv := res;
end;



function addammo(num: integer; var count: integer): integer;
var
  i: integer;
begin
  for i := 1 to MAXITEMS do
  begin
    if (data.inventory[i].number = num) and (data.inventory[i].count < MAXCOUNT) then
    begin
      repeat
        inc(data.inventory[i].count);
        dec(count);
      until (count = 0) or (data.inventory[i].count = MAXCOUNT)
    end;
    if count <= 0 then
      break;
  end;
  for i := 1 to MAXITEMS do
    if data.inventory[i].number = 0 then
      break;
  if (count > 0) and (num > 0) and (data.inventory[i].number = 0) then
  begin
    data.inventory[i].number := num;
    data.inventory[i].count := count;
    count := 0;
  end
  else if count > 0 then
    hudmessage(STR_CANT_CARRY_ANYTHING, MT_TEXT);
  addammo := count;
end;


function getmysila(monster: word): integer;
var
  item: TItem;
  enemy: TEnemy;
  k: integer;
  res: integer;
  torandom, temprandom: Integer;
  tempustal: Integer;
begin
  mon_info(monster, enemy);
  item.Effect := 0;
  res := 0;
  if (data.r_hand <> 0) then
  begin
    iteminfo(data.r_hand, item);
    res := item.effect;
    if (not item.IsShooting)
      res := res + data.strength
    else
      res := res + data.weapon - item.expa;
  end
  else
    res := data.strength;

  if (data.Desease > 0) then
    dec(res, data.Desease);

  if (data.r_hand <> 0) then
  begin
    case item.itemtype of
      ITPrimitive, ITBows: res := res - (enemy.protect_base div 2) - Random(enemy.protect_base div 2);
      ITFire, ITShells, ITRockets, ITNapalm: res := res - (enemy.protect_fire div 2) - Random(enemy.protect_fire div 2) - Random(enemy.protect_base div 3);
      ITElectric, ITCell: res := res - (enemy.protect_electric div 2) - Random(enemy.protect_electric div 2) - Random(enemy.protect_base div 3);
      ITPsi: res := res - (enemy.protect_psi div 2) - Random(enemy.protect_psi div 2);
    end;
  end
  else
    res := res - (enemy.protect_base div 2) - Random(enemy.protect_base div 2);
  if res < 0 then
    res := 0;
  getmysila := res;
end;


function getnpcname(i: word): string;
var
  enemy: TEnemy;
begin
  mon_info(i, enemy);
  getnpcname := enemy.name;
end;


procedure saveizm;
var
  f: file of level_rec;
  date: TDate;
begin
  date := StrToDate(inttostr(data.day) + '.' + inttostr(data.month) + '.' + inttostr(data.year));
  cur_lev.LastVisit := date;
  if (data.PrisonLevel > data.LastSavedLevel) then
    data.LastSavedLevel := data.PrisonLevel;
  if (cur_lev.groza) and (random(10) > 6) then
    cur_lev.groza := false;
  assignfile(f, MAPFOLDER + data.location + SAZ_EXTENSION);
  rewrite(f);
  System.write(f, cur_lev);
  closefile(f);
  DoCompress(MAPFOLDER + data.location + SAZ_EXTENSION, MAPFOLDER + data.location + SAZ_EXTENSION + ARC_EXTENSION);
  erase(f);
end;


function IsDverka(x, y: byte): boolean;
begin
  IsDverka := CheckForOut(x, y) and IsType(x, y, 'Door');
end;

function IsDoorClosedForMonster(x, y, monx, mony: integer): boolean;
var
  enemy: TEnemy;
begin
  Result := (cur_lev.map[x, y].levAttrz <> 0) or (cur_lev.map[x, y].DoorCode <> 0);
  if not Result then
  begin
    mon_info(cur_lev.map[monx, mony].Monster.montype, enemy);
    if not IsAutoOpen(x, y) then
      Result := not CanOpenDoors(enemy.Attrz)
    else
      Result := not CanOpenAutoDoors(enemy.Attrz);
  end;
end;

function DoorClosed(x, y: byte): boolean;
var
  cx, cy: byte;
  acode: string;
  error: integer;
  code: integer;
  res: boolean;
  i, j: integer;
begin
  DoorClosed := true;
  res := ((cur_lev.map[x, y].levAttrz <> 0) and (not hasitem(cur_lev.map[x, y].levAttrz)));
  if res then
    exit
  else if ((cur_lev.map[x, y].levAttrz = 0) and (cur_lev.map[x, y].DoorCode = 0)) then
    DoorClosed := false
  else if (cur_lev.map[x, y].levAttrz = 0) then
  begin
    cx := wherex;
    cy := wherey;
    clrscr;
    textcolor(white);
    write(STR_INPUT_CODE);
    textcolor(LightGray);
    readln(acode);
    val(acode, code, error);
    if (error <> 0) or (code <> cur_lev.map[x, y].DoorCode) then
    begin
      textcolor(LightRed);
      writeln(STR_INVALID_CODE);
      readkey;
    end
    else
    begin
      cur_lev.map[x, y].DoorCode := 0;
      DoorClosed := false;
      textcolor(LightGreen);
      writeln(STR_CORRECT_CODE);
      readkey;
    end;
    clrscr;
    gotoxy(cx, cy);
  end
  else
  begin
    DoorClosed := false;
    hudmessage(STR_OPENED, MT_TEXT);
    j := cur_lev.map[x, y].levAttrz;
    cur_lev.map[x, y].levAttrz := 0;
    if ((j > 428) and (j < 433)) then
    begin
      for i := 1 to MAXITEMS do
        if (data.inventory[i].number = j) then
        begin
          data.inventory[i].number := 0;
          data.inventory[i].count := 0;
          RegularizeThings;
          exit;
        end;
    end;
  end;
end;


procedure dverka(x, y: shortint);
var
  tx, ty: byte;
begin
  door := false;
  closed := false;
  tx := wherex;
  ty := wherey;
  if (IsDverka(tx + x, ty + y)) then
  begin
    if (not DoorClosed(tx + x, ty + y)) then
    begin
      door := true;
      SetElement(tx + x, ty + y, 'Door_open');
      gotoxy(tx, ty);
    end
    else
    begin
      door := true;
      if cur_lev.map[tx + x, ty + y].DoorCode = 0 then
      begin
        gotoxy(1, RESOLUTION_Y + 1);
        textcolor(lightred);
        if (cur_lev.map[tx + x, ty + y].levAttrz = 429) then
          hudmessage(STR_CLOSED_BLUE, MT_ERROR, false)
        else if (cur_lev.map[tx + x, ty + y].levAttrz = 430) then
          hudmessage(STR_CLOSED_GREEN, MT_ERROR, false)
        else if (cur_lev.map[tx + x, ty + y].levAttrz = 431) then
          hudmessage(STR_CLOSED_RED, MT_ERROR, false)
        else if (cur_lev.map[tx + x, ty + y].levAttrz = 432) then
          hudmessage(STR_CLOSED_GRAY, MT_ERROR, false)
        else if (cur_lev.map[tx + x, ty + y].levAttrz = 433) then
          hudmessage(STR_CLOSED_MAGENTA, MT_ERROR, false)
        else
          hudmessage(STR_CLOSED, MT_ERROR, false);
        gotoxy(tx, ty);
      end
      else
        gotoxy(tx, ty);
    end;
  end;
end;


function MonsterOnScreen: boolean;
var
  i, j: byte;
begin
  MonsterOnScreen := true;
  for j := 1 to RESOLUTION_Y do
    for i := 1 to RESOLUTION_X do
      if (IsThisAMonster(i, j)) and (cur_lev.map[i, j].Monster.montype > FRIEND) then
        exit;
  MonsterOnScreen := false;
end;

procedure pri_hear;
var
  ax, ay: byte;
  enemy: TEnemy;
begin
  if (data.Ed > 0) then
    exit;
  ax := wherex;
  ay := wherey;
  if (cur_lev.map[ax, ay].voiceType < FRIEND) and (cur_lev.map[ax, ay].voiceType <> VOICE_HERO)
     and (cur_lev.map[ax, ay].voice > (15 - data.vnimanie)) and (cur_lev.map[ax, ay].VoiceType <> 0) and (cur_lev.map[ax, ay].voiceType <> data.lastvoice) then
  begin
    mon_info(cur_lev.map[ax, ay].voiceType, enemy);
    data.lastvoice := cur_lev.map[ax, ay].voiceType;
    if MonstersInView = 0 then
      hudmessage(STR_YOU_HEAR + enemy.name, MT_TEXT, false);
  end;
end;

procedure prihear_bez;
var
  ax, ay: byte;
  enemy: TEnemy;
begin
  ax := wherex;
  ay := wherey;
  mon_voice;
  if (cur_lev.map[ax, ay].voiceType < FRIEND) and (cur_lev.map[ax, ay].voiceType <> VOICE_HERO)
     and (cur_lev.map[ax, ay].VoiceType <> 0) and (cur_lev.map[ax, ay].voice > (12 - data.vnimanie)) then
  begin
    mon_info(cur_lev.map[ax, ay].voiceType, enemy);
    data.lastvoice := cur_lev.map[ax, ay].voiceType;
    hudmessage(STR_YOU_HEAR + enemy.name, MT_TEXT);
  end;
end;

procedure pri_smell;
var
  ax, ay: byte;
  enemy: TEnemy;
begin
  ax := WhereX;
  ay := WhereY;
  if (cur_lev.map[ax, ay].ZapahType < FRIEND) and (cur_lev.map[ax, ay].ZapahType <> ZAPAH_HERO)
     and (cur_lev.map[ax, ay].Zapah > (500 - (data.vnimanie * 5))) and (cur_lev.map[ax, ay].ZapahType <> data.lastzapah) then
  begin
    mon_info(cur_lev.map[ax, ay].ZapahType, enemy);
    data.lastzapah := cur_lev.map[ax, ay].ZapahType;
    hudmessage(STR_YOU_SMELL + enemy.name, MT_TEXT);
  end
  else
  begin
    hudmessage(STR_YOU_SMELL_NOTHING, MT_TEXT);
    data.lastzapah := 0;
  end;
end;

procedure prismell_bez;
var
  ax, ay: byte;
  enemy: TEnemy;
begin
  ax := WhereX;
  ay := WhereY;
  if (cur_lev.map[ax, ay].ZapahType < FRIEND) and (cur_lev.map[ax, ay].ZapahType <> ZAPAH_HERO)
     and (cur_lev.map[ax, ay].Zapah > (500 - (data.vnimanie * 5))) then
  begin
    mon_info(cur_lev.map[ax, ay].ZapahType, enemy);
    data.lastzapah := cur_lev.map[ax, ay].ZapahType;
    hudmessage(STR_YOU_SMELL + enemy.name, MT_TEXT);
  end
  else
  begin
    hudmessage(STR_YOU_SMELL_NOTHING, MT_TEXT);
    data.lastzapah := 0;
  end;
end;

function FindAmmo(num: integer): integer;
var
  i: integer;
begin
  FindAmmo := 0;
  for i := 1 to MAXITEMS do
    if (data.inventory[i].number = num) then
      Result := Result + data.inventory[i].count;
end;

function HasAmmoLeft: integer;
var
  item: TItem;
begin
  iteminfo(data.r_hand, item);
  HasAmmoLeft := 0;
  case item.itemtype of
    ITBows: HasAmmoLeft := FindAmmo(AMMO_BOWS);
    ITFire: HasAmmoLeft := FindAmmo(AMMO_FIRE);
    ITShells: HasAmmoLeft := FindAmmo(AMMO_SHELLS);
    ITNapalm: HasAmmoLeft := FindAmmo(AMMO_NAPALM);
    ITElectric: HasAmmoLeft := FindAmmo(AMMO_ELECTRICAL);
    ITRockets: HasAmmoLeft := FindAmmo(AMMO_ROCKETS);
    ITCell: HasAmmoLeft := FindAmmo(AMMO_CELLCHARGE);
    ITPsi: HasAmmoLeft := FindAmmo(AMMO_PSI);
  end;
end;

procedure hud;
var
  ax, ay: byte;
  bx, by: byte;
  i: byte;
  enemy: TEnemy;
  item: TItem;
  s: string;
begin
  ax := wherex;
  ay := wherey;
  GoToXY(1, RESOLUTION_Y + 1);
  TextColor(White);
  Write(cur_lev.name);
  i := RESOLUTION_X - 15 - Length(STR_HEALTH) - Length(STR_PRISONLEVEL);
  while (WhereX < i) do
    write(' ');
  TextColor(White);
  TextColor(LightGray);
  if ((screen[1, RESOLUTION_Y + 3] <> '') and (screen[1, RESOLUTION_Y + 3] <> ' ')) then
  begin
    GoToXY(1, RESOLUTION_Y + 3);
    while (WhereX < RESOLUTION_X) do
      write(' ');
  end;
  if (screen[1, RESOLUTION_Y + HUDSIZE] <> '+') then
  begin
    GoToXY(1, RESOLUTION_Y + 5);
    write('+');
    while (WhereX < RESOLUTION_X) do
      write('-');
    write('+');
    GoToXY(1, RESOLUTION_Y + 8);
    write('+');
    while (WhereX < RESOLUTION_X) do
      write('-');
    write('+');
  end;
  GoToXY(1, RESOLUTION_Y + 6);
  write('| ');
  i := RESOLUTION_Y - 4;
  if ((not data.IsInInv) and (data.prizrak) and (screen[1, RESOLUTION_Y + 4] in [#0, ' '])) then
  begin
    bx := WhereX;
    by := WhereY;
    GoToXY(1, RESOLUTION_Y + 4);
    write(STR_SUICIDE);
    while WhereX < RESOLUTION_X do
      write(' ');
    GoToXY(bx, by);
  end
  else if ((not data.IsInInv) and (not data.prizrak) and (screen[1, RESOLUTION_Y + 4] in [#0, ' '])) then
  begin
    bx := WhereX;
    by := WhereY;
    GoToXY(1, RESOLUTION_Y + 4);
    while (WhereX <= RESOLUTION_X) do
      Write(' ');
    if (data.ustal < 10000) then
    begin
      GoToXY(2, RESOLUTION_Y + 4);
      if (data.ustal < 100) then
        TextColor(Red)
      else if (data.ustal < 1000) then
        TextColor(Yellow)
      else
        TextColor(White);
      write(STR_HUD_USTAL);
    end;
    if (data.hunger < 10000) then
    begin
      GoToXY(13, RESOLUTION_Y + 4);
      if (data.hunger < 100) then
        TextColor(Red)
      else if (data.hunger < 1000) then
        TextColor(Yellow)
      else
        TextColor(White);
      write(STR_HUD_HUNGRY);
    end;
    if (data.bloodchosen > 0) then
    begin
      GoToXY(20, RESOLUTION_Y + 4);
      if (data.bloodchosen > 10) then
        TextColor(Red)
      else if (data.bloodchosen > 5) then
        TextColor(Yellow)
      else
        TextColor(White);
      write(STR_HUD_BLOODCHOSEN);
    end;
    if (data.bolezn > 0) then
    begin
      GoToXY(34, RESOLUTION_Y + 4);
      if (data.bolezn > 10) then
        TextColor(Red)
      else if (data.bolezn > 5) then
        TextColor(Yellow)
      else
        TextColor(White);
      write(STR_HUD_BOLEZN);
    end;
    TextColor(Red);
    if (data.Strah) then
    begin
      GoToXY(43, RESOLUTION_Y + 4);
      write(STR_HUD_STRAH);
    end;
    if (data.Blind > 0) then
    begin
      GoToXY(50, RESOLUTION_Y + 4);
      write(STR_HUD_BLIND);
    end;
    if (data.Ed > 0) then
    begin
      GoToXY(59, RESOLUTION_Y + 4);
      write(STR_HUD_ED);
    end;
    if (data.palsy > 0) then
    begin
      GoToXY(68, RESOLUTION_Y + 4);
      write(STR_HUD_PALSY);
    end;
    if (data.Poisoned > 0) then
    begin
      GoToXY(77, RESOLUTION_Y + 4);
      write(STR_HUD_POISONED);
    end;
    if (data.Contusioned > 0) then
    begin
      GoToXY(89, RESOLUTION_Y + 4);
      write(STR_HUD_CONTUSIONED);
    end;
    TextColor(LightGray);
  end;
  GoToXY(bx, by);
  while ((i > 0) and (data.lastMessages[i] = '')) do
    dec(i);
  if ((i > 0) and (data.LastMessages[i] <> '')) then
  begin
    if (data.LastMessages[RESOLUTION_Y - 3] = '') then
      write(data.lastmessages[i])
    else if (i > 1) then
      write(data.lastmessages[i - 1]);
  end;
  while (WhereX < RESOLUTION_X) do
    write(' ');
  write('|');
  if (data.LastMessages[RESOLUTION_Y - 3] <> '') then
    data.LastMessages[RESOLUTION_Y - 3] := ''
  else
  begin
    GoToXY(1, RESOLUTION_Y + 7);
    TextColor(LightGray);
    write('|');
    while (WhereX < RESOLUTION_X) do
      write(' ');
    write('|');
  end;
  TextColor(White);
  GoToXY(1, RESOLUTION_Y + 2);
  if (data.r_hand <> 0) then
  begin
    iteminfo(data.r_hand, item);
    if (item.IsShooting) then
    begin
      textcolor(LightGray);
      if (item.InOboyma > 0) then
        write(STR_IN_YOUR_HANDS + item.name + ' (' + IntToStr(data.InRozhok) + '\' + IntToStr(item.InOboyma) + ') [' + IntToStr(HasAmmoLeft) + ']')
      else
        write(STR_IN_YOUR_HANDS + item.name + ' [' + IntToStr(HasAmmoLeft) + ']');
    end
    else
      write(STR_IN_YOUR_HANDS + item.name);
  end
  else
    Write(STR_IN_YOUR_HANDS);
  while (WhereX < RESOLUTION_X) do
    write(' ');
  GoToXY(RESOLUTION_X - 30, RESOLUTION_Y + 2);
  s := GetTime;
  Write(GetDate + ' ' + Copy(s, 1, Length(s) - 3));
  textcolor(green);
  gotoxy(RESOLUTION_X - 15 - Length(STR_HEALTH) - Length(STR_PRISONLEVEL), RESOLUTION_Y + 1);
  write(STR_HEALTH);
  if data.health > data.max_health then
    textcolor(cyan)
  else if data.health >= data.max_health div 3
    then textcolor(green)
  else if data.health > data.max_health div 10 then
    textcolor(yellow)
  else
    textcolor(red);
  if (data.health < 10) then
    write('   ')
  else if (data.health < 100) then
    write('  ')
  else if (data.health < 1000) then
    write(' ');
  write(IntToStr(data.health));
  if (data.expoints > 0) then
  begin
   TextColor(LightRed);
   write('  +  ');
  end;
  if data.PrisonLevel > 0 then
  begin
    textcolor(LightGray);
    write(STR_PRISONLEVEL + IntToStr(data.prisonLevel));
  end;
  While (WhereX < RESOLUTION_X) do
    write(' ');
  if not MonsterOnScreen then
  begin
    if (cur_lev.map[ax, ay].ZapahType < FRIEND) and (cur_lev.map[ax, ay].ZapahType <> ZAPAH_HERO)
       and (cur_lev.map[ax, ay].Zapah > (500 - (data.vnimanie * 2))) and (cur_lev.map[ax, ay].ZapahType <> data.lastzapah) then
    begin
      mon_info(cur_lev.map[ax, ay].ZapahType, enemy);
      data.lastzapah := cur_lev.map[ax, ay].ZapahType;
      hudmessage(STR_YOU_SMELL + enemy.name, MT_TEXT);
    end
    else if (cur_lev.map[ax, ay].ZapahType <> data.lastzapah) then
      data.lastzapah := 0;
  end;
  gotoxy(ax, ay);
end;

function daytime: byte;
begin
  daytime := 0;
  case data.hours of
    0..4 : daytime := DT_NIGHT;
    5..9 : daytime := DT_MORNING;
    10..16 : daytime := DT_DAY;
    17..22 : daytime := DT_EVENING;
    23..24 : daytime := DT_NIGHT;
  end;
end;


function streetcol: byte;
begin
  streetcol := 0;
  case daytime of
    DT_NIGHT: streetcol := darkgray;
    DT_MORNING:
    begin
      if (not cur_lev.dark) and (not cur_lev.groza) then
        streetcol := lightgray
      else
        streetcol := darkgray;
    end;
    DT_EVENING:
    begin
      if (not cur_lev.dark) and (not cur_lev.groza) then
        streetcol := lightgray
      else
        streetcol := darkgray;
    end;
    DT_DAY:
    begin
      if (not cur_lev.dark) and (not cur_lev.groza) then
        streetcol := lightgray
      else
        streetcol := darkgray;
    end;
  end;
end;


function molchun(x, y: byte): boolean;
var
  i: integer;
begin
  molchun := false;
  if cur_lev.map[x, y].Monster.montype = 60001 then
    exit;
  for i := 1 to Length(KEY_HERO_TYPES) do
    if (KEY_HERO_TYPES[i] = cur_lev.map[x, y].Monster.montype) then
      exit;
  molchun := true;
end;

procedure mon_info(monname: string; var enemy: TEnemy);
var
  i: integer;
begin
  enemy := EmptyEnemy;
  for i := 1 to MonstersCount do
    if MonArr[i].name = monname then
      break;
  if MonArr[i].name = monname then
    enemy := MonArr[i];
end;

procedure mon_info(monclass: word; var enemy: TEnemy);
var
  i: integer;
begin
  enemy := EmptyEnemy;
  for i := 1 to MonstersCount do
    if MonArr[i].montype = monclass then
      break;
  if MonArr[i].montype = monclass then
    enemy := MonArr[i];
end;


procedure dead(s: string);
begin
  clrscr;
  gotoxy(((RESOLUTION_X div 2) - (length(s))) div 2, (RESOLUTION_Y div 2) + (RESOLUTION_Y mod 2));
  textcolor(yellow);
  write(s);
  if DontDeleteSaves then
  begin
    if readkey = KEY_LOAD then
      loadgame
    else
      halt;
  end
  else
  begin
    readkey;
    DeleteSaves;
    DeleteSave;
    DeletePlayer;
    halt;
  end;
end;

procedure deczapah;
var
  i, j: byte;
begin
  for j := 1 to RESOLUTION_Y do
  begin
    for i := 1 to RESOLUTION_X do
    begin
      if cur_lev.map[i, j].Zapah > 0 then
      begin
        dec(cur_lev.map[i, j].Zapah);
        if cur_lev.map[i, j].Zapah = 0 then
          cur_lev.map[i, j].ZapahType := 0;
      end;
    end;
  end;
end;

function itemscount: byte;
var
  res: byte;
begin
  res := 1;
  Result := 0;
  while (res < MAXITEMS + 1) do
  begin
    if data.inventory[res].number <> 0 then
      inc(result);
    inc(res);
  end;
end;



function vokrug(x, y: integer; TypeName: string): integer;
begin
  Result := 0;
  if (IsType(x - 1, y - 1, TypeName)) then
    inc(Result);
  if (IsType(x - 1, y, TypeName)) then
    inc(Result);
  if (IsType(x - 1, y + 1, TypeName)) then
    inc(Result);
  if (IsType(x, y - 1, TypeName)) then
    inc(Result);
  if (IsType(x, y + 1, TypeName)) then
    inc(Result);
  if (IsType(x + 1, y - 1, TypeName)) then
    inc(Result);
  if (IsType(x + 1, y, TypeName)) then
    inc(Result);
  if (IsType(x + 1, y + 1, TypeName)) then
    inc(Result);
end;

procedure iteminfo(num: integer; var info: TItem);
var
  i: integer;
begin
  info := EmptyItem;
  if num = 0 then
    exit;
  for i := 1 to MaxItemsCount do
    if (ItemsArr[i].number = num) then
    begin
      info := ItemsArr[i];
      exit;
    end;
end;


function hasitem(item: integer): boolean;
var
  f: byte;
begin
  hasitem := true;
  if (item = 0) then
    exit;
  for f := 1 to MAXITEMS do
    if data.inventory[f].number = item then
      exit;
  hasitem := false;
end;

function DeleteProbels(s: string): string;
var
  i, j: integer;
begin
  for i := 1 to Length(s) do
    if (s[i] <> ' ') then
      for j := Length(s) downto 1 do
        if s[j] <> ' ' then
        begin
          DeleteProbels := Copy(s, i, j - i + 1);
          exit;
        end;
end;

procedure AddMessageToHistory(s: string);
var
  i: integer;
begin
  for i := 1 to RESOLUTION_Y - 4 do
  begin
    if data.LastMessages[i] = '' then
    begin
      data.LastMessages[i] := s;
      data.LastMessages[RESOLUTION_Y - 3] := s;
      exit;
    end;
  end;
  for i := 1 to RESOLUTION_Y - 5 do
    data.LastMessages[i] := data.lastMessages[i + 1];
  data.LastMessages[RESOLUTION_Y - 4] := s;
  data.LastMessages[RESOLUTION_Y - 3] := s;
end;

procedure hudmessage(text: string; msgtype: byte; KeyRead: boolean = true; KeyBefore: boolean = false);
var
  cx, cy, i: byte;
  tex: string;
  KeyRead1: boolean;
begin
  KeyRead1 := ((data.LastMessages[RESOLUTION_Y - 3] <> '') and (text <> '') and (not data.IsInInv) and (not KeyBefore));
  if (msgType <> MT_ITEM) then
    data.LastMessages[RESOLUTION_Y - 3] := text;
  tex := '';
  for cx := 1 to length(text) do
    if text[cx] = '_' then
      tex := tex + ' '
    else
      tex := tex + text[cx];
  cx := wherex;
  cy := wherey;
  if (tex <> '') and (msgType <> MT_ITEM) and (not data.IsInInv) then
    AddMessageToHistory(tex);
  if data.IsInInv then
  begin
    tex := DeleteProbels(tex);
    gotoxy(1, RESOLUTION_Y + 1);
    for i := 1 to ((RESOLUTION_X - Length(tex)) div 2) - 2 do
      tex := ' ' + tex;
  end
  else if (msgType <> MT_ITEM) then
    gotoxy(1, RESOLUTION_Y + 7)
  else
    GoToXY(1, RESOLUTION_Y + 3);
  TextColor(LightGray);
  if (data.IsInInv) then
    write('[')
  else if (msgType <> MT_ITEM) then
    write('| ');
  case msgtype of
    MT_TEXT: textcolor(white);
    MT_INFO: textcolor(white);
    MT_COOL: textcolor(lightgreen);
    MT_WARNING: textcolor(yellow);
    MT_ERROR: textcolor(lightred);
    MT_CRITICAL: textcolor(red);
    MT_ITEM: textcolor(LightGray)
  end;
  if (KeyRead1) then
    readkey;
  write(tex);
  while (WhereX < RESOLUTION_X) do
    write(' ');
  TextColor(LightGray);
  if (data.IsInInv) then
    write(']')
  else if (msgType <> MT_ITEM) then
    write('|');
  if (KeyRead) and (data.IsInInv) then
    readkey;
  gotoxy(cx, cy);
end;


function getname(num: integer): string;
var
  item: TItem;
begin
  iteminfo(num, item);
  if item.number = num then
    getname := item.name;
end;

procedure loadlevel;
var
  txt: file of level_rec;
  findx, findy: byte;
  enemy: TEnemy;
begin
  if ArchiveExists(MAPFOLDER + data.location + LEV_EXTENSION) then
    DoUnCompress(MAPFOLDER + data.location + LEV_EXTENSION + ARC_EXTENSION, MAPFOLDER + data.location + LEV_EXTENSION);
  assignfile(txt, MAPFOLDER + data.location + LEV_EXTENSION);

  {$I-}
    reset(txt);
  {$I+}

  if IOResult <> 0 then
    Abort(STR_ERROR_OPENING_FILE + MAPFOLDER + data.location + LEV_EXTENSION);
  read(txt, cur_lev);
  closefile(txt);
  if ArchiveExists(MAPFOLDER + data.location + LEV_EXTENSION) then
    erase(txt);
  if ArchiveExists(MAPFOLDER + data.location + SAZ_EXTENSION) then
    DoUncompress(MAPFOLDER + data.location + SAZ_EXTENSION + ARC_EXTENSION, MAPFOLDER + data.location + SAZ_EXTENSION);
  assignfile(txt, MAPFOLDER + data.location + SAZ_EXTENSION);

  {$I-}
    reset(txt);
  {$I+}

  if ioresult = 0 then
  begin
    read(txt, cur_lev);
    closefile(txt);
    if ArchiveExists(MAPFOLDER + data.location + SAZ_EXTENSION) then
      erase(txt);
  end
  else
  begin
    if ArchiveExists(MAPFOLDER + data.location + SAV_EXTENSION) then
      DoUncompress(MAPFOLDER + data.location + SAV_EXTENSION + ARC_EXTENSION, MAPFOLDER + data.location + SAV_EXTENSION);
    assignfile(txt, MAPFOLDER + data.location + SAV_EXTENSION);

    {$I-}
      reset(txt);
    {$I+}

    if ioresult = 0 then
    begin
      read(txt, cur_lev);
      closefile(txt);
      if ArchiveExists(MAPFOLDER + data.location + SAV_EXTENSION) then
        erase(txt);
    end;
  end;
  for findx := 1 to RESOLUTION_X do
  begin
    for findy := 1 to RESOLUTION_Y do
    begin
      if (cur_lev.map[findx, findy].Monster.montype <> 0) and (cur_lev.map[findx, findy].Monster.health > 0) then
      begin
        mon_info(cur_lev.map[findx, findy].Monster.montype, enemy);
        if cur_lev.map[findx, findy].Monster.monchar = #0 then
          cur_lev.map[findx, findy].Monster.monchar := enemy.character;
      end;
    end;
  end;
  SpecialGeneration;
end;

procedure ClearMessageHistory;
var
  i: integer;
begin
  for i := 1 to RESOLUTION_Y - 3 do
    data.LastMessages[i] := '';
end;

procedure loadgame(IsExit: boolean = false);
var
  f : file of dataz;
  s: string;
begin
  s := GetSaveFolder;
  assignfile(f, GetSaveFolder + ARC_SAVEFILENAME);
  {$I-}
    Reset(F);
  {$I+}
  if IOResult <> 0 then
  begin
    if (IsExit) then
      Abort(STR_CANT_OPEN_SAVEGAME)
    else
      Error(STR_CANT_OPEN_SAVEGAME);
  end
  else
  begin
    CloseFile(F);
    DoUnCompress(GetSaveFolder + ARC_SAVEFILENAME, GetSaveFolder + SAVEFILENAME);
    AssignFile(f, GetSaveFolder + SAVEFILENAME);
    Reset(f);
    Read(F, data);
    closefile(f);
    Erase(f);
    DeleteAll('*.sav*');
    DeleteAll('*.saz*');
    DeleteAll('prison*.*');
    DeleteAll('random.*');
    CopyAll('*.sav*', s, MAPFOLDER);
    CopyAll('*.saz*', s, MAPFOLDER);
    CopyAll('prison*.*', s, MAPFOLDER);
    CopyAll('random.*', s, MAPFOLDER);
    clrscr;
    loadlevel;
    ClearMessageHistory;
  end;
end;

procedure itemz;
var
  item: TItem;
  s: string;
  i: integer;
begin
  iteminfo(cur_lev.map[wherex, wherey].item, item);
  s := item.name;
  if ((item.number >= TECHNO_MIN) and (item.number <= TECHNO_MAX)) then
  begin
    i := data.technoZ[cur_lev.map[wherex, wherey].item - TECHNO_MIN + 1].num;
    s := STR_TECHNOZ[i] + STR_TECHNO;
    if (data.TechnoZ[cur_lev.map[wherex, wherey].item - TECHNO_MIN + 1].examined) then
      s := s + item.name;
  end
  else if ((item.number >= FLOPPY_MIN) and (item.number <= FLOPPY_MAX)) then
  begin
    i := data.nanoz[cur_lev.map[wherex, wherey].item - FLOPPY_MIN + 1].num;
    s := STR_PROG + ' ';
    if data.NanoZ[cur_lev.map[wherex, wherey].item - FLOPPY_MIN + 1].examined then
      s := s + item.name
    else
      s := s + STR_NANOZ[i];
  end
  else if ((item.number >= DRINK_MIN) and (item.number <= DRINK_MAX)) then
  begin
    s := ' ' + STR_DRINK;
    if data.DrinkZ[cur_lev.map[wherex, wherey].item - DRINK_MIN + 1].examined then
      s := s + item.name
    else
      s := STR_DRINKZ[data.DrinkZ[cur_lev.map[wherex, wherey].item - DRINK_MIN + 1].num] + s;
  end
  else if (item.IsShooting) and (item.InOboyma > 0) then
    s := s + ' (' + inttostr(cur_lev.map[wherex, wherey].ItemCount) + '\' + inttostr(item.InOboyma) + ')'
  else if (cur_lev.map[wherex, wherey].ItemCount > 1) then
    s := s + ' (' + inttostr(cur_lev.map[wherex, wherey].ItemCount) + ')';
  if item.name <> '' then
    hudmessage(s, MT_ITEM, false);
end;

procedure readm(var item: TItem; num: integer);
var
  itemZ: TItem;
begin
  item.name := '';
  item.number := 0;
  item.Effect := 0;
  item.cost := 0;
  iteminfo(num, itemz);
  if itemz.number = num then
    itemz := item
end;

procedure vidall;
var
  i, j: byte;
begin
  for j := 1 to RESOLUTION_Y do
    for i := 1 to RESOLUTION_X do
      if not IsInHouse(i, j) then
      begin
        if (data.location = 'random') and (IsInSkala(i, j)) then
          cur_lev.map[i, j].vid := '^'
        else
          cur_lev.map[i, j].vid := cur_lev.map[i, j].lev;
      end;
end;


End.
