unit svet;

interface

uses
  windows, sysutils, loadz, sun, monsters, voice;

procedure clean_all;
procedure clean_map_all;
procedure osv_map;
procedure osv(color: byte = White; MakeMonsterMove: boolean = true; PaintHero: boolean = true; IsClean: boolean = true);
function mon_attack(aclass: integer; health: integer; monx, mony: byte): integer;
procedure mon_move;
procedure ItemWay(x1, y1: integer; var x2, y2: integer; num, count: integer);
procedure Los(x1, y1: integer; var x2, y2: integer);
procedure LookAround(zx, zy, color: byte);


implementation

uses
  MyCRT, LevGen, attack;

var
  arr: array[1..RESOLUTION_X, 1..RESOLUTION_Y] of char;
  ar: array[1..RESOLUTION_X, 1..RESOLUTION_Y] of integer;

function Bigger(a, b: byte): byte;
begin
  if (a > b) then
    Bigger := a
  else
    Bigger := b;
end;


function CastRay(x, y, r: byte; angle: integer): TCoord;
var
  c: TCoord;
begin
  c.x := round(x + r * Cosinus[Round(angle / 360 * SINCOSCOUNT)]);
  c.y := round(y - r * Sinus[Round(angle / 360 * SINCOSCOUNT)]);
  Result := c;
end;

procedure PutItem(oldx, oldy: integer; anItem: TSlot);
var
  weapon_uron: integer;
  item: Titem;
begin
  iteminfo(anItem.number, item);
  weapon_uron := Random(100) + 1;
  if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item.number <> 0) or (item.itemtype = ITNanoRobot) then
  begin
    data.IsInInv := false;
    hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT);
  end
  else
    cur_lev.map[oldx, oldy].item := anItem;
end;

{$Hints Off}
procedure ItemWay(x1, y1: integer; var x2, y2: integer; num, count: integer);
var
  dx, dy, sdx, sdy, dxabs, dyabs, x, y, px, py: integer;
  oldx, oldy: integer;
  item: TItem;
  enemy: TEnemy;
  weapon_uron: integer;
  s: string;
  udar: integer;
  strength_koeff: integer;
const
  ITEM_SLEEP = 40;
begin
  iteminfo(num, item);
  strength_koeff := data.strength * 10;
  if x2 < 1 then
    x2 := 1;
  if y2 < 1 then
    y2 := 1;
  dx := x2 - x1;
  dy := y2 - y1;
  dxabs := abs(dx);
  dyabs := abs(dy);
  sdx := Sgn(dx);
  sdy := Sgn(dy);
  x := dyabs div 2;
  y := dxabs div 2;
  px := x1;
  py := y1;
  oldx := px;
  oldy := py;
  if (dxabs >= dyabs) then
  begin
    while true do
    begin
      y := y + dyabs;
      if (y >= dxabs) then
      begin
        y := y - dxabs;
        oldy := py;
        py := py + sdy;
      end;
      oldx := px;
      px := px + sdx;
      if IsType(px, py, 'Window') then
      begin
        dec(strength_koeff, item.weight);
        if (strength_koeff >= 0) then
        begin
          if (not IsBoldWin(px, py)) then
            SetElement(px, py, 'Window_crashed')
          else
          begin
            weapon_uron := Random(100) + 1;
            if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
              hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
            else
              PutItem(oldx, oldy, item, count);
          end;
          inc(strength_koeff, item.weight);
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
          begin
            data.IsInInv := false;
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT);
            exit;
          end;
        end
        else
        begin
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
          else
            PutItem(oldx, oldy, item, count);
          RepaintScreen;
          exit;
        end;
      end
      else if ((IsBold(px, py)) and (not IsType(px, py, 'Window_crashed'))) or (IsThisAMonster(px, py)) then
      begin
        if IsThisAMonster(px, py) and (cur_lev.map[px, py].Monster.montype < FRIEND) then
        begin
          udar := 120 - (data.metkost div 2);
          if udar > 0 then
            udar := Random(udar)
          else
            udar := 99;
          if (udar < 100) then
          begin
            mon_info(cur_lev.map[px, py].Monster.montype, enemy);
            weapon_uron := (item.weight - enemy.protect_base * 2 + (strength_koeff div 5)) div item.weight;
            if weapon_uron < 1 then
              weapon_uron := 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(weapon_uron) + STR_POINTS2 + PointsSklon(weapon_uron);
              decmonhealth(px, py, weapon_uron);
              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);
              weapon_uron := Random(100) + 1;
              if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
                hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
              else
                PutItem(oldx, oldy, item, count);
              RepaintScreen;
              exit;
            end;
            weapon_uron := Random(100) + 1;
            if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
              hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
            else
              PutItem(oldx, oldy, item, count);
          end;
          RepaintScreen;
        end
        else if (IsThisAMonster(px, py)) and (cur_lev.map[px, py].Monster.montype >= FRIEND) then
        begin
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
          else
            PutItem(oldx, oldy, item, count);
          RepaintScreen;
          exit
        end
        else if IsBold(px, py) then
        begin
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
          else
            PutItem(oldx, oldy, item, count);
          RepaintScreen;
          exit;
        end;
      end;
      dec(strength_koeff, item.weight);
      if strength_koeff < 0 then
      begin
        weapon_uron := Random(100) + 1;
        if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
          hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
        else
          PutItem(oldx, oldy, item, count);
        RepaintScreen;
        osvkletka(oldx, oldy);
        exit;
      end
      else
      begin
        PutSymbol(px, py, item.ItemColor, item.ItemChar);
        RepaintScreen;
      end;
      Sleep(ITEM_SLEEP);
      osvkletka(px, py);
    end;
  end
  else
  begin
    while true do
    begin
      x := x + dxabs;
      if (x > dyabs) then
      begin
        x := x - dyabs;
        oldx := px;
        px := px + sdx;
      end;
      oldy := py;
      py := py + sdy;
      if IsType(px, py, 'Window') then
      begin
        dec(strength_koeff, item.weight);
        if strength_koeff >= 0 then
        begin
          if (not IsBoldWin(px, py)) then
            SetElement(px, py, 'Window_crashed')
          else
          begin
            weapon_uron := Random(100) + 1;
            if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
              hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
            else
              PutItem(oldx, oldy, item, count);
          end;
          inc(strength_koeff, item.weight);
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
          begin
            data.IsInInv := false;
            hudmessage(STR_ITEM + GetInventoryName(item.number)+ STR_DESTROYED, MT_TEXT);
            exit;
          end;        
        end
        else
        begin
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
          else
            PutItem(oldx, oldy, item, count);
          RepaintScreen;
        end;
        exit;
      end
      else if ((IsBold(px, py)) and (not IsType(px, py, 'Window_crashed'))) or (IsThisAMonster(px, py)) then
      begin
        if IsThisAMonster(px, py) and (cur_lev.map[px, py].Monster.montype < FRIEND) then
        begin
          udar := 120 - (data.metkost div 2);
          if udar > 0 then
            udar := Random(udar)
          else
            udar := 99;
          if (udar < 100) then
          begin
            mon_info(cur_lev.map[px, py].Monster.montype, enemy);
            weapon_uron := (item.weight - enemy.protect_base * 2 + (strength_koeff div 5)) div item.weight;
            if weapon_uron < 1 then
              weapon_uron := 1;
            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(weapon_uron) + STR_POINTS2 + PointsSklon(weapon_uron);
            decmonhealth(px, py, weapon_uron);
            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);
            weapon_uron := Random(100) + 1;
            if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
              hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
            else
              PutItem(oldx, oldy, item, count);
            RepaintScreen;
            exit;
          end;
        end
        else if (IsThisAMonster(px, py)) and (cur_lev.map[px, py].Monster.montype >= FRIEND) then
        begin
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
          else
            PutItem(oldx, oldy, item, count);
          exit
        end
        else if IsBold(px, py) then
        begin
          weapon_uron := Random(100) + 1;
          if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
            hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
          else
            PutItem(oldx, oldy, item, count);
          RepaintScreen;
          exit;
        end;
      end;
      dec(strength_koeff, item.weight);
      if strength_koeff < 0 then
      begin
        weapon_uron := Random(100) + 1;
        if (weapon_uron <= item.Frailty) or (cur_lev.map[oldx, oldy].item <> 0) or (item.itemtype = ITNanoRobot) then
          hudmessage(STR_ITEM + GetInventoryName(item.number) + STR_DESTROYED, MT_TEXT)
        else
          PutItem(oldx, oldy, item, count);
        RepaintScreen;
        exit;
      end
      else
      begin
        PutSymbol(px, py, item.ItemColor, item.ItemChar);
        RepaintScreen;
      end;
      Sleep(ITEM_SLEEP);
      osvkletka(px, py);
    end;
  end;
end;
{$Hints On}

procedure Los(x1, y1: integer; var x2, y2: integer);
var
  i, dx, dy, sdx, sdy, dxabs, dyabs, x, y, px, py: integer;
  curLight: integer;
  beforeStop: integer;
begin
  curLight := cur_lev.map[x1, y1].Light;
  beforeStop := -1;
  if x2 < 1 then
    x2 := 1;
  if y2 < 1 then
    y2 := 1;
  dx := x2 - x1;
  dy := y2 - y1;
  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
    for i := 1 to dxabs do
    begin
      y := y + dyabs;
      if (y >= dxabs) then
      begin
        y := y - dxabs;
        py := py + sdy;
      end;
      px := px + sdx;
      osvkletka_now(px, py);
      osvkletka(px, py);
      if (beforeStop <> -1) then
        dec(beforeStop);
      if (cur_lev.map[px, py].Light < curLight) then
        if ((beforeStop > cur_lev.map[px, py].Light) or (beforeStop = -1)) then
          beforeStop := cur_lev.map[px, py].Light;
      if (IsSeeStopping(px, py) or (beforeStop = 0)) then
        exit;
    end;
  end
  else
  begin
    for i := 1 to dyabs do
    begin
      x := x + dxabs;
      if (x > dyabs) then
      begin
        x := x - dyabs;
        px := px + sdx;
      end;
      py := py + sdy;
      osvkletka_now(px, py);
      osvkletka(px, py);
      if (beforeStop <> -1) then
        dec(beforeStop);
      if (cur_lev.map[px, py].Light < curLight) then
        if ((beforeStop > cur_lev.map[px, py].Light) or (beforeStop = -1)) then
          beforeStop := cur_lev.map[px, py].Light;
      if (IsSeeStopping(px, py) or (beforeStop = 0)) then
        exit;
    end;
  end;
end;

procedure Clean_kletka(findx, findy: byte);
var
  color: byte;
begin
  if (cur_lev.map[findx, findy].vid <> #0) then
  begin
    if data.hardly then
      color := black
    else if (cur_lev.street) and (not cur_lev.map[findx, findy].shadow) then
      color := streetcol
    else
      color := SHADOW_COLOR;
    if MarkAtMap(findx, findy) then
      PutSymbol(findx, findy, color, cur_lev.map[findx, findy].vid)
    else
      PutSymbol(findx, findy, color, GetFloorChar);
  end
  else
    PutSymbol(findx, findy, black, ' ');
end;

procedure LookAround(zx, zy, color: byte);
var
  c: TCoord;
  i: integer;
  cx, cy: byte;
  x, y: byte;
  tx, ty: integer;
begin
  cx := wherex;
  cy := wherey;
  gotoxy(zx, zy);
  for i := 0 to 72 do
  begin
    c := CastRay(cx, cy, data.cur_osv, i * 5);
    Los(cx, cy, c.x, c.y);
  end;
  tx := zx - data.cur_osv - 2;
  ty := zy - data.cur_osv - 2;
  x := tx;
  y := ty;
  if not (x in [1..RESOLUTION_X]) then
    x := 1;
  if not (y in [1..RESOLUTION_Y]) then
    y := 1;
  tx := x;
  ty := y;
  for y := ty to zy + data.cur_osv + 2 do
    for x := tx to zx + data.cur_osv + 2 do
      if (x in [1..RESOLUTION_X]) and (y in [1..RESOLUTION_Y]) then
      begin
        if (cur_lev.map[x, y].now in [#0, ' ']) then
          Clean_kletka(x, y)
        {else
          osvkletka(x, y, color);}
      end;
  gotoxy(cx, cy);
end;

procedure clean_all;
var
  color: byte;
  findx, findy: integer;
begin
  gotoxy(wherex - 1, wherey - 1);
  xc := wherex;
  yc := wherey;
  clrscr;
  for findy := 1 to RESOLUTION_Y do
  begin
    for findx := 1 to RESOLUTION_X do
    begin
      if (cur_lev.map[findx, findy].vid <> #0) then
        cur_lev.map[findx, findy].now := #0;
      if (cur_lev.map[findx, findy].vid <> #0) then
      begin
        if data.hardly then
          color := MAP_HARD_COLOR
        else if (cur_lev.street) and (not cur_lev.map[findx, findy].shadow) then
          color := streetcol
        else if (cur_lev.street) and (cur_lev.map[findx, findy].shadow) then
          color := SHADOW_COLOR
        else
          color := MAP_COLOR;
        if MarkAtMap(findx, findy) then
        begin
          if cur_lev.map[findx, findy].shadow then
            color := darkgray;
          PutSymbol(findx, findy, color, cur_lev.map[findx, findy].vid);
        end
        else
        begin
          if cur_lev.map[findx, findy].shadow then
            color := darkgray;
          PutSymbol(findx, findy, color, GetFloorChar);
        end;
        cur_lev.map[findx, findy].now := #0;
      end;
    end;
  end;
  gotoxy(xc + 1, yc + 1);
end;

procedure clean_map_all;
var
  findx, findy: integer;
  aCol: integer;
begin
  for findy := 1 to RESOLUTION_Y do
    for findx := 1 to RESOLUTION_X do
    begin
      cur_lev.map[findx, findy].now := #0;
      if (cur_lev.map[findx, findy].vid <> #0) then
      begin
        if (data.hardly) then
          aCol := Black
        else
          aCol := DarkGray;
        PutSymbol(findx, findy, aCol, cur_lev.map[findx, findy].vid);
      end;
    end;
end;

procedure osv_map;
var
  r: integer;
begin
  r := data.cur_osv;
  data.cur_osv := 2;
  osv;
  data.cur_osv := r;
end;

procedure clean_now;
var
  x, y: byte;
begin
  for y := 1 to RESOLUTION_Y do
    for x := 1 to RESOLUTION_X do
      cur_lev.map[x, y].now := #0;
end;

procedure osv(color: byte = White; MakeMonsterMove: boolean = true; PaintHero: boolean = true; IsClean: boolean = true);
var
  x, y: byte;
  radius: integer;
begin
  data.posx := whereX;
  data.posy := whereY;
  data.lookatstreet := false;
  if data.location = MAP_MAIN then
  begin
    data.invisible := 0;
    data.prizrak := false;
  end;
  if data.health <= 0 then
  begin
    if (data.prizrak) then
    begin
      data.prizrak := false;
      data.health := data.hum_h;
      data.hum_h := 0;
      gotoxy(data.hum_x, data.hum_y);
      data.hum_x := 0;
      data.hum_y := 0;
      clean_all;
      osv(data.Light_color)
    end
    else
      dead(STR_DEAD);
  end;
  if (IsClean) then
  begin
    for y := 1 to RESOLUTION_Y do
      for x := 1 to RESOLUTION_X do
        arr[x, y] := cur_lev.map[x, y].now;
    clean_now;
  end;
  if data.infrared then
    color := red;
  data.lookatstreet := false;
  xc := wherex;
  yc := wherey;
  radius := data.cur_osv;
  if data.Blind > 0 then
    data.cur_osv := 1;
  LookAround(wherex, wherey, color);
  data.cur_osv := radius;
  if (cur_lev.street) and (data.lastsunhour <> data.hours) then
  begin
    data.lastsunhour := data.hours;
    clearshadows;
    sunlight(data.hours);
  end;
  if (cur_lev.map[xc, yc].Light > 0) then
    data.cur_osv := cur_lev.map[xc, yc].Light;
  gotoxy(xc, yc);
  if PaintHero then
  begin
    if (color <> white) then
      textcolor(color)
    else if data.invisible > 0 then
      textcolor(INVISIBLE_COLOR)
    else if data.prizrak then
      textcolor(GHOST_COLOR)
    else
      textcolor(data.herocolor);
    write(data.hero);
    gotoxy(wherex - 1, wherey);
  end
  else
    OsvKletka(wherex, wherey, color);
  if (data.location <> MAP_MAIN) and (MakeMonsterMove) then
  begin
    mon_move;
    gotoxy(data.posx, data.posy);
  end;    
end;

function mon_attack(aclass: integer; health: integer; monx, mony: byte): integer;
var zzx: byte;
    temp1: integer;
    enemy: TEnemy;
begin
  mon_info(aclass, enemy);
  result:=health;
  zzx := Random(100);
  if (zzx < enemy.rasseyannost) then
  begin
    case enemy.sex of
      Male: hudmessage(enemy.name + ' ' + STR_MISS, MT_ERROR, false);
      Female: hudmessage(enemy.name + ' ' + STR_MISS_SHE, MT_ERROR, false);
      Other: hudmessage(enemy.name + ' ' + STR_MISS_IT, MT_ERROR, false);
    end;
    exit;
  end;
  if (enemy.strength = 0) then
    exit;
  if (abs(monx-data.posx) > 1) or (abs(mony-data.posy) > 1) then
    exit;
  gotoxy(1, RESOLUTION_Y + 1);
  textcolor(lightred);
  temp1 := random(getsila(aclass));
  if temp1 > 0 then
  begin
    case enemy.sex of
      Male: hudmessage(getclass(aclass) + STR_RANIL_YOU + inttostr(temp1) + STR_POINTS + PointsSklon(temp1), MT_ERROR, true);
      Female: hudmessage(getclass(aclass) + STR_RANILA_YOU + inttostr(temp1) + STR_POINTS + PointsSklon(temp1), MT_ERROR, true);
      Other: hudmessage(getclass(aclass) + STR_RANILO_YOU + inttostr(temp1) + STR_POINTS + PointsSklon(temp1), MT_ERROR, true);
    end;
    readkey;
  end
  else
    hudmessage(STR_NOT_RANIL_YOU, MT_ERROR, true);
  while wherex < 45 do
    write(' ');
  dec(data.health, temp1);
  if random(255) <= enemy.chance_bolen then
  begin
    if data.bolezn < 255 then
      inc(data.bolezn);
    if data.bolezn = 1 then
      hudmessage(STR_YOU_ARE_ILL, MT_INFO);
  end
  else if random(255) <= enemy.chance_blood then
  begin
    if data.bloodchosen < 255 then
      inc(data.bloodchosen);
    if data.bloodchosen = 1 then
      hudmessage(STR_ARE_BLOODCHOSEN, MT_INFO);
  end;
  mon_attack := result;
end;

procedure FriendGo(monx, mony: byte);
begin
  if (abs(data.posx - monx) > 1) and (abs(data.posy - mony) > 1) and (Random(10) mod 3 = 0) then
    movemon(monx, mony, Random(3) - 1, Random(3) - 1);
end;

procedure GoAwayFromHero(monx, mony: integer);
var
  hodx, hody: shortint;
begin
  if (monx > data.posx) and (not sten(monx + 1, mony)) then
    hodx := 1
  else if (monx < data.posx) and (not sten(monx - 1, mony)) then
    hodx := -1
  else
    hodx := Random(3) - 1;
  if (data.posy < mony) and (not sten(monx + hodx, mony + 1)) then
    hody := 1
  else if (mony < data.posy) and (not sten(monx + hodx, mony - 1)) then
    hody := -1
  else
    hody := Random(3) - 1;
  if not sten(monx + hodx, mony + hody) then
    movemon(monx, mony, hodx, hody);
end;

procedure initarr;
var
  i, j: integer;
begin
  for j := 1 to RESOLUTION_Y do
    for i := 1 to RESOLUTION_X do
      ar[i, j] := 0;
end;

procedure GoWave(x, y, i, j, step: integer; iter: Integer);
var
  enemy: TEnemy;
  dx, dy: shortint;
begin
  if (iter > MAXITERATIONS) then
    exit;
  mon_info(cur_lev.map[i, j].Monster.montype, enemy);
  if not (x in [1..RESOLUTION_X]) or not (y in [1..RESOLUTION_Y]) then
    exit;
  if step > 20 then
    exit;
  if (ar[x, y] = 0) or (ar[x, y] > step) then
    ar[x, y] := step
  else
    exit;
  if (x = i) and (y = j) then
    exit;
  for dy := -1 to 1 do
    for dx := -1 to 1 do
      if (dx = 0) and (dy = 0) then
      else
        if (not IsBold(x + dx, y + dy)) and (not IsType(x + dx, y + dy, 'Door')) then
          GoWave(x + dx, y + dy, i, j, step + 1, iter + 1)
        else if ((IsType(x + dx, y + dy, 'Door')) and (not IsDoorClosedForMonster(x + dx, y + dy, i, j))) or (IsType(x + dx, y + dy, 'Window')) or (IsType(x + dx, y + dy, 'Window_crashed')) then
          GoWave(x + dx, y + dy, i, j, step + 1, iter + 1)
end;

procedure FindAWay(x, y: integer);
var
  i, j: integer;
begin
  i := cur_lev.map[x, y].Monster.point div 256;
  j := cur_lev.map[x, y].Monster.point mod 256;
  GoWave(i, j, x, y, 1, 0);
end;

function VokrugAr(monx, mony: integer): integer;
begin
  Result := 0;
  if (monx > 1) and (ar[monx - 1, mony] > 0) then
    inc(Result);
  if (monx > 1) and (mony > 1) and (ar[monx - 1, mony - 1] > 0) then
    inc(Result);
  if (monx > 1) and (mony < RESOLUTION_Y) and (ar[monx - 1, mony + 1] > 0) then
    inc(Result);
  if (monx < RESOLUTION_X) and (ar[monx + 1, mony] > 0) then
    inc(Result);
  if (monx < RESOLUTION_X) and (mony > 1) and (ar[monx + 1, mony - 1] > 0) then
    inc(Result);
  if (monx < RESOLUTION_X) and (mony < RESOLUTION_Y) and (ar[monx + 1, mony + 1] > 0) then
    inc(Result);
  if (mony > 1) and (ar[monx, mony - 1] > 0) then
    inc(Result);
  if (mony < RESOLUTION_Y) and (ar[monx, mony + 1] > 0) then
    inc(Result);
end;

procedure CollectAr(x, y: integer; var dx, dy: shortint);
var
  i: integer;
begin
  i := maxint;
  if (ar[x - 1, y - 1] <> 0) and (ar[x - 1, y - 1] < i) then
  begin
    i := ar[x - 1, y - 1];
    dx := -1;
    dy := -1;
  end;
  if (ar[x - 1, y] <> 0) and (ar[x - 1, y] < i) then
  begin
    i := ar[x - 1, y];
    dx := -1;
    dy := 0;
  end;
  if (ar[x - 1, y + 1] <> 0) and (ar[x - 1, y + 1] < i) then
  begin
    i := ar[x - 1, y + 1];
    dx := -1;
    dy := 1;
  end;
  if (ar[x, y - 1] <> 0) and (ar[x, y - 1] < i) then
  begin
    i := ar[x, y - 1];
    dx := 0;
    dy := -1;
  end;
  if (ar[x, y + 1] <> 0) and (ar[x, y + 1] < i) then
  begin
    i := ar[x, y + 1];
    dx := 0;
    dy := 1;
  end;
  if (ar[x + 1, y - 1] <> 0) and (ar[x + 1, y - 1] < i) then
  begin
    i := ar[x + 1, y - 1];
    dx := 1;
    dy := -1;
  end;
  if (ar[x + 1, y] <> 0) and (ar[x + 1, y] < i) then
  begin
    i := ar[x + 1, y];
    dx := 1;
    dy := 0;
  end;
  if (ar[x + 1, y + 1] <> 0) and (ar[x + 1, y + 1] < i) then
  begin
    dx := 1;
    dy := 1;
  end;
end;

procedure PrintArr;
var
  i, j: integer;
begin
  clrscr;
  for j := 1 to RESOLUTION_Y do
    for i := 1 to RESOLUTION_X do
      write(inttostr(ar[i, j])[1]);
  readkey;
end;

procedure GoToPoint(monx, mony: integer);
var
  hodx, hody: shortint;
begin
  initarr;
  FindAWay(monx, mony);
  hodx := 0;
  hody := 0;
  if Vokrugar(monx, mony) = 0 then
  begin
    cur_lev.map[monx, mony].Monster.point := 0;
    exit;
  end;
  Collectar(monx, mony, hodx, hody);
  movemon(monx, mony, hodx, hody);
  if ((monx + hodx) = cur_lev.map[monx + hodx, mony + hody].Monster.point div 256) and
    ((mony + hody) = cur_lev.map[monx + hodx, mony + hody].Monster.point mod 256) then
      cur_lev.map[monx + hodx, mony + hody].Monster.point := 0;
end;

procedure GoToHeroLastViewPoint(monx, mony: integer);
begin
  cur_lev.map[monx, mony].Monster.point := data.posx * 256 + data.posy;
  GoToPoint(monx, mony);
end;

function IsHeroInView(monx, mony: integer): boolean;
begin
  IsHeroInView :=
    (cur_lev.map[monx, mony].now <> #0) and (data.invisible <= 0);
end;

function MonsterScareHero(monx, mony: integer): boolean;
begin
  MonsterScareHero := (cur_lev.map[monx, mony].Monster.health <
    (gethealth(cur_lev.map[monx, mony].Monster.montype) div 3))
      and (data.invisible <= 0);
end;

function IsZapah(monx, mony: integer): integer;
var
  enemy: TEnemy;
begin
  IsZapah := 0;
  mon_info(cur_lev.map[monx, mony].Monster.montype, enemy);
  if (cur_lev.map[monx, mony].Zapah >= enemy.nyuh) then
    IsZapah := cur_lev.map[monx, mony].ZapahType;
end;

function IsVoice(monx, mony: integer): integer;
var
  enemy: TEnemy;
begin
  IsVoice := 0;
  mon_info(cur_lev.map[monx, mony].Monster.montype, enemy);
  if (cur_lev.map[monx, mony].Voice >= enemy.sluh) then
    IsVoice := cur_lev.map[monx, mony].VoiceType;
end;

procedure GetZaklEffect(x, y: integer);
begin
end;

procedure move_human(x, y: byte);
var
  hodx, hody: shortint;
  cx, cy: integer;
  enemy: TEnemy;
begin
  mon_info(cur_lev.map[x, y].Monster.montype, enemy);
  MakeHeroNoise(x, y, cur_lev.map[x, y].Monster.montype, enemy.VoiceStrength);
  if (cur_lev.map[x, y].Monster.Zak.ZakLong > 0) then
  begin
    GetZaklEffect(x, y);
    dec(cur_lev.map[x, y].Monster.Zak.ZakLong);
    if (cur_lev.map[x, y].Monster.Zak.ZakLong = 0) then
      cur_lev.map[x, y].Monster.Zak.ZakType := 0;
  end;
  if (IsHeroInView(x, y)) and (data.sleep) then
    dead(STR_YOU_WAS_KILLED);
  if (MonsterScareHero(x, y)) and (IsHeroInView(x, y)) then
    GoAwayFromHero(x, y)
  else if (IsHeroInView(x, y)) then
    GoToHeroLastViewPoint(x, y)
  else if (cur_lev.map[x, y].Monster.point <> 0) then
    GoToPoint(x, y)
  else if not (MonsterScareHero(x, y)) and (IsVoice(x, y) = VOICE_HERO) then
  begin
    cx := x;
    cy := y;
    selectvoice(cx, cy, hodx, hody, enemy.strength, CanOpenDoors(enemy.Attrz));
    cur_lev.map[x, y].Monster.point := cx * 256 + cy;
  end
  else if IsVoice(x, y) = VOICE_HERO then
  begin
    selectvoice_obr(x, y, hodx, hody, enemy.strength, CanOpenDoors(enemy.Attrz));
    movemon(x, y, hodx, hody);
  end
  else if not (MonsterScareHero(x, y)) and (IsZapah(x, y) = ZAPAH_HERO) then
  begin
    selectzapah(x, y, hodx, hody, enemy.strength, CanOpenDoors(enemy.Attrz));
    if (x + hodx = data.posx) and (y + hody = data.posy) then
      mon_attack(cur_lev.map[x, y].Monster.montype, cur_lev.map[x, y].Monster.health, x, y)
    else
      movemon(x, y, hodx, hody);
  end
  else if not (IsHeroInView(x, y)) and (IsZapah(x, y) = ZAPAH_HERO) then
  begin
    selectzapah_obr(x, y, hodx, hody, enemy.strength, CanOpenDoors(enemy.Attrz));
    movemon(x, y, hodx, hody);
  end
  else
    movemon(x, y, Random(3) - 1, Random(3) - 1);
end;

function Rasst(x1, x2: byte; y1, y2: byte): word;
begin
  Rasst := Round(sqrt((x1 - x2) * (x1 - x2)) + sqrt((y1 - y2) * (y1 - y2)));
end;

procedure move_human_shooting(x, y: byte);
var
  IsShoot: boolean;
  enemy: TEnemy;
  item: TItem;
  hodx, hody: shortint;
begin
  IsShoot := ((Random(10) + 1) mod 3 = 0);
  mon_info(cur_lev.map[x, y].Monster.montype, enemy);
  iteminfo(cur_lev.map[x, y].Monster.item, item);
  if (cur_lev.map[x, y].Monster.count = 0) and (IsHeroInView(x, y)) and (not MonsterScareHero(x, y)) then
  begin
    hudmessage(enemy.name + STR_MONSTER_RELOAD, MT_TEXT);
    cur_lev.map[x, y].Monster.count := item.InOboyma;
  end
  else if (IsHeroInView(x, y)) and (not MonsterScareHero(x, y)) and (data.sleep) then
    dead(STR_YOU_WAS_KILLED_WHILE_SLEEPING)
  else if (IsHeroInView(x, y)) then
  begin
    if (MonsterScareHero(x, y)) then
      GoAwayFromHero(x, y)
    else if not IsShoot then
    begin
      if ((Random(10) + 1) mod 3 = 0) then
        GoToHeroLastViewPoint(x, y)
      else
        GoAwayFromHero(x, y);
    end
    else
      mon_shoot(x, y);
  end
  else
  begin
    if (MonsterScareHero(x, y)) and (IsZapah(x, y) = ZAPAH_HERO) then
    begin
      selectzapah_obr(x, y, hodx, hody, enemy.strength, CanOpenDoors(enemy.Attrz));
      movemon(x, y, hodx, hody);
    end
    else if (cur_lev.map[x, y].Monster.point <> 0) then
      GoToPoint(x, y)
    else
      movemon(x, y, Random(3) - 1, Random(3) - 1);
  end;
end;

procedure move_zomby_shooting(x, y: byte);
var
  item: TItem;
  enemy: TEnemy;
begin
  if (cur_lev.map[x, y].Monster.count <= 0) then
  begin
    mon_info(cur_lev.map[x, y].Monster.montype, enemy);
    iteminfo(cur_lev.map[x, y].Monster.item, item);
    cur_lev.map[x, y].Monster.count := item.InOboyma;
    if (IsHeroInView(x, y)) then
      hudmessage(enemy.name + STR_MONSTER_RELOAD, MT_TEXT);
  end
  else if ((Random(10) + 1) mod 3 = 0) and (IsHeroInView(x, y)) then
    mon_shoot(x, y)
  else
    movemon(x, y, Random(3) - 1, Random(3) - 1);
end;

procedure move_grib(x, y: byte);
var
  enemy: TEnemy;
begin
  if (abs(x - data.posx) <= 1) and (abs(y - data.posy) <= 1) then
  begin
    mon_info(cur_lev.map[x, y].Monster.montype, enemy);  
    hudmessage(enemy.name + STR_GRIB_ATTACK, MT_TEXT);
    mon_attack(enemy.montype, cur_lev.map[x, y].Monster.health, x, y);
  end;
end;

procedure move_worm(x, y: byte);
var
  enemy: TEnemy;
begin
  mon_info(cur_lev.map[x, y].Monster.montype, enemy);
  MakeHeroNoise(x, y, cur_lev.map[x, y].Monster.montype, enemy.VoiceStrength);
  if (cur_lev.map[x, y].Monster.Zak.ZakLong > 0) then
  begin
    GetZaklEffect(x, y);
    dec(cur_lev.map[x, y].Monster.Zak.ZakLong);
    if (cur_lev.map[x, y].Monster.Zak.ZakLong = 0) then
      cur_lev.map[x, y].Monster.Zak.ZakType := 0;
  end;
  if (IsHeroInView(x, y)) and (data.sleep) then
    dead(STR_YOU_WAS_KILLED_WHILE_SLEEPING)
  else if (IsHeroInView(x, y)) then
    GoToHeroLastViewPoint(x, y)
  else if (cur_lev.map[x, y].Monster.point <> 0) then
    GoToPoint(x, y)
  else
    movemon(x, y, Random(3) - 1, Random(3) - 1);
end;

procedure turret_move(x, y: byte);
var
  enemy: TEnemy;
  item: TItem;
begin
  if (cur_lev.map[x, y].Monster.count <= 0) then
  begin
    mon_info(cur_lev.map[x, y].Monster.montype, enemy);
    iteminfo(cur_lev.map[x, y].Monster.item, item);
    if (IsHeroInView(x, y)) then
      hudmessage(enemy.name + STR_MONSTER_RELOAD, MT_TEXT);
    cur_lev.map[x, y].Monster.count := item.InOboyma;
  end
  else if (IsHeroInView(x, y)) then
    mon_shoot(x, y);
end;

procedure bomzh_move(x, y: byte);
var
  Show: boolean;
  enemy: TEnemy;
begin
  if (not IsHeroInView(x, y)) then
  begin
    if (abs(x - data.posx) <= 1) and (abs(y - data.posy) <= 1) then
    begin
      Show := (Random(300 - data.vnimanie) < 150);
      if (Show) then
      begin
        mon_info(cur_lev.map[x, y].monster.montype, enemy);
        hudmessage(enemy.name + STR_BOMZH_NOT_STEAL, MT_TEXT, true);
      end
    end;
  end
  else
    movemon(x, y, Random(3) - 1, Random(3) - 1);
end;

procedure cat_move(x, y: byte);
begin
  if (abs(data.posx - x) > 1) or (abs(data.posy - y) > 1) then
    movemon(x, y, Random(3) - 1, Random(3) - 1)
  else if ((Random(10) + 1) mod 2 = 0) then
    GoAwayFromHero(x, y);
end;

procedure dog_move(x, y: byte);
begin
  if (abs(data.posx - x) > 1) or (abs(data.posy - y) > 1) then
    movemon(x, y, Random(3) - 1, Random(3) - 1)
  else if ((Random(10) + 1) mod 2 = 0) then
    GoAwayFromHero(x, y);
end;

procedure idiot_move(x, y: byte);
begin
  movemon(x, y, Random(3) - 1, Random(3) - 1);
end;

procedure beggar_move(x, y: byte);
begin
  if not (IsHeroInView(x, y)) then
    movemon(x, y, Random(3) - 1, Random(3) - 1);
end;

procedure mon_move;
var
  x, y: byte;
  enemy: TEnemy;
  item: TItem;
begin
  for y := 1 to RESOLUTION_Y do
    for x := 1 to RESOLUTION_X do
      if IsThisAMonster(x, y) and not (cur_lev.map[x, y].Monster.shod) then
      begin
        mon_info(cur_lev.map[x, y].Monster.montype, enemy);
        case enemy.Intellect of
          AIHuman:
            if cur_lev.map[x, y].Monster.montype < FRIEND then
            begin
              if cur_lev.map[x, y].Monster.item <> 0 then
              begin
                iteminfo(cur_lev.map[x, y].Monster.item, item);
                if item.IsShooting then
                  move_human_shooting(x, y)
                else
                  move_human(x, y);
              end
              else
                move_human(x, y)
            end
            else
              FriendGo(x, y);
          AIGrib: move_grib(x, y);
          AIWorm: move_worm(x, y);
          AITurret: turret_move(x, y);
          AIBomzh: bomzh_move(x, y);
          AICat: cat_move(x, y);
          AIDog: dog_move(x, y);
          AIIdiot: idiot_move(x, y);
          AIBeggar: beggar_move(x, y);
          AIZomby:
            if (cur_lev.map[x, y].Monster.item <> 0) then
            begin
              iteminfo(cur_lev.map[x, y].Monster.item, item);
              if item.IsShooting then
                move_zomby_shooting(x, y)
              else
                move_human(x, y);
            end
            else
              move_human(x, y);
        end;
        cur_lev.map[x, y].Monster.shod := true;
      end;
  for x := 1 to RESOLUTION_X do
    for y := 1 to RESOLUTION_Y do
      cur_lev.map[x, y].Monster.shod := false;
end;

begin
end.