unit GameSys;
//----------------------------------------------------------------------------------------------------------------------
// Alex 'Shirson' Nevskiy 2012-08
//----------------------------------------------------------------------------------------------------------------------
interface
uses SysUtils, _var, _render, LevelSys, InfoScreen;// eXgine;

type

  TPrm = record
    Current,
    Maximum:integer;
  end;

  TPortal = record
    Coord:TPoint;
    State:byte;
  end;

  TMuzzleFlash = record
    Coord:Tpoint;
    FG,BG,Ch:integer;
    Delay:integer;
  end;

  TProjectile=record
    Coord: TPoint;
    Target: TPoint;
    Dist:single;
    RawX, RawY:single;
    dx, dy: single;
    TypeOf: byte;
    Damage:single;
    TTL:integer;
    FG, BG:byte;
    Ch:byte;
  end;

  TEntity = record
    Coord:TPoint;
    Planed:TPoint;
    Target:Tpoint;
    Shoot:boolean;
    HP: TPrm;
    ViewRange:TPrm;
    Armor: TPrm;
    Ammo: array[1..5] of TPrm;
    Weapon: array[1..5] of boolean;
    WeaponReady: array[1..5] of integer;
    CurrentWeapon:integer;
    CurrentTarget:integer;
    DamageTaken:single;
    MuzzleFlash:TMuzzleFlash;
    TypeOf:integer;
    exp:integer;
    shout:integer;
    MoveDelay:integer;
  end;

  TFx = record
    X,Y:integer;
    FG: byte;
    BG: byte;
    Block: byte;
    Delay:integer;
  end;

const
    entPlayer=1;
    SwithDelay=60;
    ptSmoke=1;
    ptRocket=2;
    ptBlastWave=3;

    totalExp=26;
    enTotal = 40;
    enHigh= 10;
    enBossLevel=5;
    enStart1=1;
    enStart2=2; //2
    enStart3=1;
    enStart4=5; //5

    wpLevelSG=1;
    wpLevelMG=1;
    wpLevelRL=4; //4

var CurrentLevel: Tlevel;
    _Player: TEntity;
    isFirstTimeGame:boolean=true;
    GameDifficult:integer;
    CFilter:byte=colWhite;
    EntityAr: array of TEntity;
    ProjectileAr: array of TProjectile;
    FxAr: array of TFx;
    LevelN:integer=1;
    GMBackTo: TGameState=gsMenu;
    Portal:TPortal;
    Seed: LongInt;
    BonusInvul:integer=0;

procedure DrawGame;

implementation
uses HelpScreen, MainMenu, _math;

var ScrollX, ScrollY:integer;
      WeaponRelax: array[1..5] of integer = (30, 80, 6, 100, 50);   // 1/FireRate
      WeaponSound: array[1..5] of integer = (s_PistolShot, s_ShotGun, s_PistolShot, s_RocketL, s_Melee);
     WeaponDamage: array[1..5] of integer = (10, 20, 10, 80, 10);
  WeaponDeviation: array[1..5] of integer = (0, 3, 3, 1, 0);
       SwithReady: integer;
            Enemy: array[1..4] of TEntity;

//----------------------------------------------------------------------------------------------------------------------

procedure InitPlayer;
begin
  _Player.HP.Current:=100;
  _Player.HP.Maximum:=100;
  _Player.Armor.Current:=5;
  _Player.Armor.Maximum:=100;
  _Player.ViewRange.Current:=9;
  _Player.ViewRange.Maximum:=12;
  _Player.Ammo[1].Current:=-1;
  _Player.Ammo[1].Maximum:=-1;
  _Player.Ammo[2].Current:=0;
  _Player.Ammo[2].Maximum:=50;
  _Player.Ammo[3].Current:=0;
  _Player.Ammo[3].Maximum:=350;
  _Player.Ammo[4].Current:=0;
  _Player.Ammo[4].Maximum:=20;
  _Player.Weapon[1]:=true;
  _Player.Weapon[2]:=false;
  _Player.Weapon[3]:=false;
  _Player.Weapon[4]:=false;
  _Player.CurrentWeapon:=1;
end;

//----------------------------------------------------------------------------------------------------------------------

function GetDist(x,y:integer):single;
begin
  result:=dist(EntityAr[entPlayer].Coord.X, EntityAr[entPlayer].Coord.Y, x, y);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure InitEnemy;
begin
  //    Pistol                            ShotGun                           Melee                             Rocketman
  Enemy[1].HP.Maximum:=       5 ;  Enemy[2].HP.Maximum:=       5 ;  Enemy[3].HP.Maximum:=       5 ;   Enemy[4].HP.Maximum:=       5 ;
  Enemy[1].HP.Current:=       50;  Enemy[2].HP.Current:=       50;  Enemy[3].HP.Current:=       50;   Enemy[4].HP.Current:=       50;
  Enemy[1].Armor.Maximum:=    5;   Enemy[2].Armor.Maximum:=    5;   Enemy[3].Armor.Maximum:=    5;    Enemy[4].Armor.Maximum:=    5;
  Enemy[1].Armor.Current:=    0;   Enemy[2].Armor.Current:=    0;   Enemy[3].Armor.Current:=    0;    Enemy[4].Armor.Current:=    0;
  Enemy[1].ViewRange.Maximum:=12;  Enemy[2].ViewRange.Maximum:=12;  Enemy[3].ViewRange.Maximum:=20;   Enemy[4].ViewRange.Maximum:=15;
  Enemy[1].ViewRange.Current:=8;   Enemy[2].ViewRange.Current:=8;   Enemy[3].ViewRange.Current:=1;    Enemy[4].ViewRange.Current:=10;
  Enemy[1].CurrentWeapon:=    1;   Enemy[2].CurrentWeapon:=    2;   Enemy[3].CurrentWeapon:=    5;    Enemy[4].CurrentWeapon:=    4;
  Enemy[1].DamageTaken:=      0;   Enemy[2].DamageTaken:=      0;   Enemy[3].DamageTaken:=      0;    Enemy[4].DamageTaken:=      0;
  Enemy[1].Weapon[1]:=true;        Enemy[2].Weapon[2]:=true;        Enemy[3].Weapon[5]:=true;         Enemy[4].Weapon[4]:=true;
  Enemy[1].WeaponReady[1]:=0;      Enemy[2].WeaponReady[2]:=0;      Enemy[3].WeaponReady[5]:=0;       Enemy[4].WeaponReady[4]:=0;
  Enemy[1].Ammo[1].Current:=-1;    Enemy[2].Ammo[2].Current:=-1;    Enemy[3].Ammo[5].Current:=-1;     Enemy[4].Ammo[4].Current:=-1;
  Enemy[1].TypeOf:=1;              Enemy[2].TypeOf:=2;              Enemy[3].TypeOf:=3;               Enemy[4].TypeOf:=4;
end;

//----------------------------------------------------------------------------------------------------------------------

function CreateEnemy(TypeOf, GameDifficult, exp:integer):TEntity;
begin
  result:=Enemy[TypeOf];
  result.TypeOf:=TypeOf;
  result.exp:=exp;
  result.MoveDelay:=0;
  result.HP.Current:= result.HP.Current + result.HP.Maximum * GameDifficult + result.HP.Maximum * exp;
  result.Armor.Current:= result.Armor.Current + result.Armor.Maximum * GameDifficult + result.Armor.Maximum * exp;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddEntity(x, y, TypeOf, GameDifficult, exp:integer; Level:TLevel);
var Entity: TEntity;
    ID:integer;

    function AddToEntityAr(Entity: TEntity):integer;
    begin
      SetLength(EntityAr, High(EntityAr)+2);
      EntityAr[High(EntityAr)]:=Entity;
      result:=High(EntityAr);
    end;

begin
  Entity:=CreateEnemy(TypeOf, GameDifficult, exp);
  Entity.Coord.X:=x;
  Entity.Coord.Y:=y;
  Entity.Planed:=Entity.Coord;
  Entity.Shoot:=false;
  Entity.Shout:=0;
  ID:=AddToEntityAr(Entity);
//  if Level[x ,y].Entity<>0 then
//    ID:=ID+1;
  Level[x ,y].Entity:=ID;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure InitEntityAr;
var a:integer;
    exp:integer;
    s:string;
    TypeOf:integer;
    Px, Py:integer;
    Target:TPoint;
begin
  for Px:=0 to High(CurrentLevel) do
    for Py:=0 to High(CurrentLevel[0]) do
      CurrentLevel[Px, Py].Entity:=0;
  SetLength(EntityAr, 2); // 0 - nobody // 1 - Player
  _Player.Coord:=FoundDZ(CurrentLevel);
  _Player.Planed:=_Player.Coord;
  EntityAr[entPlayer]:=_Player;
  for a:=1 to enTotal do
  begin
    if a<=enHigh then
      exp:=LevelN
    else
      exp:=random(LevelN)+1;
    if exp > Totalexp then exp:=totalExp;
    s:='';
    if LevelN >= enStart1 then
      s:=s+'11111';
    if LevelN >= enStart2 then
      s:=s+'222';
    if LevelN >= enStart3 then
      s:=s+'333333';
    if LevelN >= enStart4 then
      s:=s+'4';
    TypeOf:=strtoint(s[random(length(s))+1]);
    Px:=EntityAr[1].Coord.X;
    Py:=EntityAr[1].Coord.Y;
    Target:=DeployEntity(PX, PY, 20, CurrentLevel);

    AddEntity(Target.X, Target.Y, TypeOf, GameDifficult, exp, CurrentLevel);
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

function isWall(Cell:byte):boolean;
begin
  result:=(Cell=blWall) or (Cell=blBedRock);
end;

//----------------------------------------------------------------------------------------------------------------------

function isVisibleCell(x0,y0,x1,y1:integer; var Level:TLevel):boolean;
var LOS: TPointsAr;
    a:integer;
begin
  result:=false;
  if not isValidCell(x1, y1, Level) then exit;
  BLine(x0, y0, x1, y1, LOS);
  result:=true;
  for a:=0 to High(LOS) do
    if isWall(Level[LOS[a].x, LOS[a].y].Block) and (a<>High(LOS)) then
      result:=false;
end;

//----------------------------------------------------------------------------------------------------------------------

function WarFog(Block:byte):byte;
begin
  case Block of
    blNone:   result:=blNoneWF;
    blWall:   result:=blWallWF;
    blBedRock:result:=blWallWF;
  else
    result:=blNoneWF;
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddFx(x, y, Delay:integer; FG, BG, Block:byte);
var Fx: TFx;

    procedure AddToFxAr(Fx: TFx);
    begin
      SetLength(FxAr, High(FxAr)+2);
      FxAr[High(FxAr)]:=Fx;
    end;

begin
  Fx.X:=X;
  Fx.Y:=Y;
  Fx.Delay:=Delay;
  Fx.FG:=FG;
  Fx.BG:=BG;
  Fx.Block:=Block;
  AddToFxAr(Fx);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddProjectile(Projectile: TProjectile);
begin
  SetLength(ProjectileAr, High(ProjectileAr)+2);
  ProjectileAr[High(ProjectileAr)]:=Projectile;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddSmoke(x, y:integer; TTL:integer; Color:byte);
var Projectile: TProjectile;
begin
  Projectile.Coord.X:=x;
  Projectile.Coord.Y:=y;
  Projectile.dx:=0;
  Projectile.dy:=0;
  Projectile.RawX:=X;
  Projectile.RawY:=Y;
  Projectile.TypeOf:=ptSmoke;
  Projectile.Damage:=0;
  Projectile.FG:=Color;
  Projectile.BG:=colAlpha;
  Projectile.Ch:=chStar8;
  Projectile.TTL:=TTL;
  AddProjectile(Projectile);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddRocket(StartX, StartY, TargetX, TargetY:integer; TTL:integer; Damage:integer; Color:byte);
var Projectile: TProjectile;
begin
  Projectile.Coord.X:=StartX;
  Projectile.Coord.Y:=StartY;
  Trajectory(StartX, StartY, TargetX, TargetY, Projectile.dx, Projectile.dy, 0);
  Projectile.dx:= Projectile.dx/AimDist*0.5;
  Projectile.dy:= Projectile.dy/AimDist*0.5;
  Projectile.RawX:=StartX;
  Projectile.RawY:=StartY;
  Projectile.TypeOf:=ptRocket;
  Projectile.Damage:=Damage;
  Projectile.FG:=Color;
  Projectile.BG:=colAlpha;
  Projectile.Ch:=chStar8;
  Projectile.TTL:=TTL;
  AddProjectile(Projectile);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddBlastWave(StartX, StartY, TargetX, TargetY:integer; Damage:single; Color:byte);
var Projectile: TProjectile;
    d:single;
begin
  Projectile.Coord.X:=StartX;
  Projectile.Coord.Y:=StartY;
  Projectile.Target.X:=StartX;
  Projectile.Target.Y:=StartY;
  Trajectory(StartX, StartY, TargetX, TargetY, Projectile.dx, Projectile.dy, 0);
  D:=dist(StartX, StartY, TargetX, TargetY);
  Projectile.dx:= Projectile.dx/AimDist*0.4*PixelP;
  Projectile.dy:= Projectile.dy/AimDist*0.4;
  Projectile.Dist:=D;
  Projectile.RawX:=StartX;
  Projectile.RawY:=StartY;
  Projectile.TypeOf:=ptBlastWave;
  Projectile.Damage:=Damage;
  Projectile.FG:=Color;
  Projectile.BG:=colAlpha;
  Projectile.Ch:=chStar8;
  Projectile.TTL:=1000;
  AddProjectile(Projectile);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AddBlast(CenterX, CenterY, Radius: integer; Damage:single);
var Ellipse: TPointsAr;
    a:integer;
    cl:byte;
    DST:single;
begin
  DST:=GetDist(CenterX, CenterY);
  PlaySnd(s_Explo, DST);
  BEllipse(CenterX, CenterY, trunc(Radius*PixelP), Radius, Ellipse);
  if High(Ellipse)>=0 then
    for a:=0 to High(Ellipse) do
    begin
      cl:=0;
      case random(2) of
        0: cl:=colLightRed;
        1: cl:=colRed;
      end;
      AddBlastWave(CenterX, CenterY, Ellipse[a].X, Ellipse[a].Y, Damage/((High(Ellipse)+1)/6), cl);
    end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure ProcessProjectileAr(var Level:Tlevel);
var a, b:integer;
    OldX,OldY:integer;
    EntityIdx:integer;
begin
  if High(ProjectileAr)>=0 then
  begin
  for a:=0 to High(ProjectileAr) do
    begin
      if ProjectileAr[a].TTL>0 then
        dec(ProjectileAr[a].TTL);

      OldX:=trunc(ProjectileAr[a].RawX);
      OldY:=trunc(ProjectileAr[a].RawY);
      ProjectileAr[a].RawX:=ProjectileAr[a].RawX + ProjectileAr[a].dx;
      ProjectileAr[a].RawY:=ProjectileAr[a].RawY - ProjectileAr[a].dy;
      ProjectileAr[a].Coord.X:=trunc(ProjectileAr[a].RawX);
      ProjectileAr[a].Coord.Y:=trunc(ProjectileAr[a].RawY);

      case ProjectileAr[a].TypeOf of
         ptSmoke:;
        ptRocket: begin
                    if (OldX<>ProjectileAr[a].Coord.X) or (OldY<>ProjectileAr[a].Coord.Y) then
                      AddSmoke(OldX, OldY, random(15)+1, colLightGray);

                    if Level[ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y].Block in [blWall, blBedrock] then
                      begin
                        ProjectileAr[a].TTL:=0;
                        AddBlast(OldX, OldY, 6, ProjectileAr[a].Damage)
                      end;
                    EntityIdx:=Level[ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y].Entity;
                    if EntityIdx<>0 then
                      begin
                        ProjectileAr[a].TTL:=0;
                        EntityAr[EntityIdx].DamageTaken:=EntityAr[EntityIdx].DamageTaken+ProjectileAr[a].Damage;
                        AddBlast(OldX, OldY, 6, ProjectileAr[a].Damage)

                        // Kabooooom!
                      end;

                  end;
     ptBlastWave: begin
                    if (Level[ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y].Blood=0) and (random(10)<3) then
                      Level[ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y].Blood:=1;
                    if (OldX<>ProjectileAr[a].Coord.X) or (OldY<>ProjectileAr[a].Coord.Y) then
                      AddSmoke(OldX, OldY, random(50)+10, colDarkGray);

                    if dist(ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y, ProjectileAr[a].Target.X, ProjectileAr[a].Target.Y)>=ProjectileAr[a].Dist then
                    //if CoordEqual(ProjectileAr[a].Coord,ProjectileAr[a].Target) then
                      ProjectileAr[a].TTL:=0;

                    if Level[ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y].Block in [blWall, blBedrock] then
                      ProjectileAr[a].TTL:=0;

                    EntityIdx:=Level[ProjectileAr[a].Coord.X, ProjectileAr[a].Coord.Y].Entity;
                    if EntityIdx<>0 then
                      begin
                        //ProjectileAr[a].TTL:=0;
                        EntityAr[EntityIdx].DamageTaken:=EntityAr[EntityIdx].DamageTaken+ProjectileAr[a].Damage;
                      end;
                  end;
      end;
    end;

    b:=0;
    for a:=0 to High(ProjectileAr) do
      if ProjectileAr[a].TTL>0 then
      begin
        ProjectileAr[b]:=ProjectileAr[a];
        inc(b);
      end;
    SetLength(ProjectileAr,b);
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure ProcessFx;//(var Level:Tlevel);
var a, b:integer;
begin
  if High(FxAr)>=0 then
  begin
    for a:=0 to High(FxAr) do
    begin
      if FxAr[a].Delay>0 then
      begin
        dec(FxAr[a].Delay);
      end
    end;
    b:=0;
    for a:=0 to High(FxAr) do
      if FxAr[a].Delay>0 then
      begin
        FxAr[b]:=FxAr[a];
        inc(b);
      end;
    SetLength(FxAr,b);
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure ShowLevel(GUI_X, GUI_Y, Width, Height, WorldX, WorldY, PlayerX, PlayerY:integer; Filter:byte; var Level: TLevel);
var x, y, a:integer;
    //isOnLOS:boolean;
begin
  for x:=0 to Width-1 do
    for y:=0 to Height-1 do
    if isValidCell(WorldX + x, WorldY + y, Level) then
      Level[WorldX + x, WorldY + y].Visible:=0;

  for x:=0 to Width-1 do
    for y:=0 to Height-1 do
      if isValidCell(WorldX + x, WorldY + y, Level) then
        if isVisibleCell(PlayerX, PlayerY, WorldX + x, WorldY + y, Level) then
        begin
          Level[WorldX + x,   WorldY + y].Visible:=1;
          if not isWall(Level[WorldX + x, WorldY + y].Block) then
          begin
            if (PlayerY+1 >= (WorldY + y)) and (PlayerX+1 >= (WorldX + x)) then
            begin
              if isValidCell(WorldX + x,   WorldY + y-1, Level) and (Level[WorldX + x,   WorldY + y-1].Block in [blWall, blBedRock]) then Level[WorldX + x,   WorldY + y-1].Visible:=2;
              if isValidCell(WorldX + x-1, WorldY + y-1, Level) and (Level[WorldX + x-1, WorldY + y-1].Block in [blWall, blBedRock]) then Level[WorldX + x-1, WorldY + y-1].Visible:=2;
              if isValidCell(WorldX + x-1, WorldY + y  , Level) and (Level[WorldX + x-1, WorldY + y  ].Block in [blWall, blBedRock]) then Level[WorldX + x-1, WorldY + y  ].Visible:=2;
            end;

            if (PlayerY+1 >= (WorldY + y)) and (PlayerX-1 <= (WorldX + x)) then
            begin
              if isValidCell(WorldX + x,   WorldY + y-1, Level) and (Level[WorldX + x,   WorldY + y-1].Block in [blWall, blBedRock]) then Level[WorldX + x,   WorldY + y-1].Visible:=2;
              if isValidCell(WorldX + x+1, WorldY + y-1, Level) and (Level[WorldX + x+1, WorldY + y-1].Block in [blWall, blBedRock]) then Level[WorldX + x+1, WorldY + y-1].Visible:=2;
              if isValidCell(WorldX + x+1, WorldY + y  , Level) and (Level[WorldX + x+1, WorldY + y  ].Block in [blWall, blBedRock]) then Level[WorldX + x+1, WorldY + y  ].Visible:=2;
            end;

            if (PlayerY-1 <= (WorldY + y)) and (PlayerX-1 <= (WorldX + x)) then
            begin
              if isValidCell(WorldX + x+1, WorldY + y  , Level) and (Level[WorldX + x+1, WorldY + y  ].Block in [blWall, blBedRock]) then Level[WorldX + x+1, WorldY + y  ].Visible:=2;
              if isValidCell(WorldX + x+1, WorldY + y+1, Level) and (Level[WorldX + x+1, WorldY + y+1].Block in [blWall, blBedRock]) then Level[WorldX + x+1, WorldY + y+1].Visible:=2;
              if isValidCell(WorldX + x,   WorldY + y+1, Level) and (Level[WorldX + x,   WorldY + y+1].Block in [blWall, blBedRock]) then Level[WorldX + x,   WorldY + y+1].Visible:=2;
            end;

            if (PlayerY-1 <= (WorldY + y)) and (PlayerX+1 >= (WorldX + x)) then
            begin
              if isValidCell(WorldX + x-1, WorldY + y  , Level) and (Level[WorldX + x-1, WorldY + y  ].Block in [blWall, blBedRock]) then Level[WorldX + x-1, WorldY + y  ].Visible:=2;
              if isValidCell(WorldX + x-1, WorldY + y+1, Level) and (Level[WorldX + x-1, WorldY + y+1].Block in [blWall, blBedRock]) then Level[WorldX + x-1, WorldY + y+1].Visible:=2;
              if isValidCell(WorldX + x,   WorldY + y+1, Level) and (Level[WorldX + x,   WorldY + y+1].Block in [blWall, blBedRock]) then Level[WorldX + x,   WorldY + y+1].Visible:=2;
            end;

//          GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=Level[WorldX + x, WorldY + y].Block;
//          GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightGray, Filter);
          end;
        end;
  for x:=0 to Width-1 do
    for y:=0 to Height-1 do
      if isValidCell(WorldX + x, WorldY + y, Level) then
      begin
        if Level[WorldX + x, WorldY + y].Visible<>0 then
        begin
          Level[WorldX + x,   WorldY + y].WarFog:=WarFog(Level[WorldX + x,   WorldY + y].Block);
          GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=Level[WorldX + x, WorldY + y].Block;
          GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightGray, Filter);
          if Level[WorldX + x,   WorldY + y].Blood>=1 then GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colDarkGray, Filter);
          if Level[WorldX + x,   WorldY + y].Blood>=2 then GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colRed, Filter);
          if Level[WorldX + x,   WorldY + y].Blood>=3 then GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=chCorpse;

          case Level[WorldX + x,   WorldY + y].Item of
            itmInvul: begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmInvul;  GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightYellow, Filter)  end;

            itmLife : begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmLife;  GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightred, Filter)  end;
            itmArmor: begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmArmor; GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightBlue, Filter)  end;

            itmAmmo  : begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmAmmo; GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colBrown, Filter)  end;
            itmAmmo+1: begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmAmmo; GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colGreen, Filter)  end;
            itmAmmo+2: begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmAmmo; GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colRed, Filter)  end;

            itmGuns  : begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmGuns;   GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colWhite, Filter)  end;
            itmGuns+1: begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmGuns+1; GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colWhite, Filter)  end;
            itmGuns+2: begin GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=itmGuns+2; GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colWhite, Filter)  end;

          end;

          if (WorldX + x = Playerx) and (WorldY + Y=PlayerY) then
          begin
            GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=byte('@');
            GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colWhite, Filter);
          end;

          if Level[WorldX + x,   WorldY + y].Entity>1 then
          begin
            case EntityAr[Level[WorldX + x,   WorldY + y].Entity].TypeOf of
              1: GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightGreen, Filter);
              2: GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightYellow, Filter);
              3: GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightMagenta, Filter);
              4: GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightCyan, Filter);
            end;
            GUIMatrix[x, y].Ch:=EntityAr[Level[WorldX + x,   WorldY + y].Entity].exp + chEnemy;;
          end;

           // GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=Level[WorldX + x,   WorldY + y].Entity;
//          if Level[WorldX + x, WorldY + y].FxBlock<>0 then GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=Level[WorldX + x, WorldY + y].FxBlock;
//          if Level[WorldX + x, WorldY + y].FxFG<>colAlpha then GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(Level[WorldX + x, WorldY + y].FxFG, Filter);
//          if Level[WorldX + x, WorldY + y].FxBG<>colAlpha then GUIMatrix[GUI_X+x, GUI_Y+y].BG:=ColorFilter(Level[WorldX + x, WorldY + y].FxBG, Filter);
          if (Portal.Coord.X=WorldX + x) and (Portal.Coord.Y=WorldY + y) then
          begin
            //if Portal.Active then
              GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colLightCyan, Filter);
            //else
            //  GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colDarkGray, Filter);
            GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=chPortal;
          end;
        end
        else
        if Level[WorldX + x,   WorldY + y].WarFog<>0 then
        begin
          GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=Level[WorldX + x, WorldY + y].WarFog;
          GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(colDarkGray, Filter);
        end;
      end;

  for a:=1 to High(EntityAr) do
  begin
    if EntityAr[a].MuzzleFlash.Delay>0 then
    begin
      dec(EntityAr[a].MuzzleFlash.Delay);
      x:= EntityAr[a].Coord.X + EntityAr[a].MuzzleFlash.Coord.X;
      y:= EntityAr[a].Coord.Y + EntityAr[a].MuzzleFlash.Coord.Y;
      if Level[x,y].Visible <>0 then
      begin
        x:= x - WorldX + GUI_X;
        y:= y - WorldY + GUI_Y;
        GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(EntityAr[a].MuzzleFlash.FG, Filter);
        GUIMatrix[GUI_X+x, GUI_Y+y].BG:=ColorFilter(EntityAr[a].MuzzleFlash.BG, Filter);
        GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=EntityAr[a].MuzzleFlash.Ch;
      end;
    end;
  end;

  if High(FxAr)>=0 then
    for a:=0 to High(FxAr) do
    begin
      x:= FxAr[a].X;
      y:= FxAr[a].Y;
      if (x=0) and (y=0) then
        FillBG(0, 0, TotalW-1, TotalH-1, colRed)
      else
      if Level[x,y].Visible <>0 then
      begin
        x:= x - WorldX + GUI_X;
        y:= y - WorldY + GUI_Y;
        if (x>=0) and (x<ViewW) and (y>=0) and (y<ViewH) then
        begin
          if FxAr[a].FG <> colAlpha then
            GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(FxAr[a].FG, Filter);
          if FxAr[a].BG <> colAlpha then
            GUIMatrix[GUI_X+x, GUI_Y+y].BG:=ColorFilter(FxAr[a].BG, Filter);
          if FxAr[a].Block <>0 then
            GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=FxAr[a].Block;
        end;
      end;
    end;

  if High(ProjectileAr)>=0 then
    for a:=0 to High(ProjectileAr) do
    begin
      x:= ProjectileAr[a].Coord.X;
      y:= ProjectileAr[a].Coord.Y;
      if Level[x,y].Visible <>0 then
      begin
        x:= x - WorldX + GUI_X;
        y:= y - WorldY + GUI_Y;
        if (x>=0) and (x<ViewW) and (y>=0) and (y<ViewH) then
        begin
          if ProjectileAr[a].FG <> colAlpha then
            GUIMatrix[GUI_X+x, GUI_Y+y].FG:=ColorFilter(ProjectileAr[a].FG, Filter);
          if ProjectileAr[a].BG <> colAlpha then
            GUIMatrix[GUI_X+x, GUI_Y+y].BG:=ColorFilter(ProjectileAr[a].BG, Filter);
          if ProjectileAr[a].Ch <>0 then
            GUIMatrix[GUI_X+x, GUI_Y+y].Ch:=ProjectileAr[a].Ch;
        end;
      end;
    end;
end;

//----------------------------------------------------------------------------------------------------------------------

function PickUpItem(CellX, CellY:integer; var Level:Tlevel):boolean;
begin
  result:=false;
  case Level[CellX, CellY].Item of
    0:;
    itmLife : begin
                if EntityAr[entPlayer].HP.Current=EntityAr[entPlayer].HP.Maximum then exit;
                inc(EntityAr[entPlayer].HP.Current, 5);
                if EntityAr[entPlayer].HP.Current>EntityAr[entPlayer].HP.Maximum then
                  EntityAr[entPlayer].HP.Current:=EntityAr[entPlayer].HP.Maximum;
                result:=true;
                PlaySnd(s_LifePickUp, 0);
              end;
    itmArmor: begin
                if EntityAr[entPlayer].Armor.Current=EntityAr[entPlayer].Armor.Maximum then exit;
                inc(EntityAr[entPlayer].Armor.Current, 5);
                if EntityAr[entPlayer].Armor.Current>EntityAr[entPlayer].Armor.Maximum then
                  EntityAr[entPlayer].Armor.Current:=EntityAr[entPlayer].Armor.Maximum;
                result:=true;
                PlaySnd(s_PickUpArmor, 0);
              end;
    itmAmmo : begin
                if EntityAr[entPlayer].Ammo[2].Current=EntityAr[entPlayer].Ammo[2].Maximum then exit;
                inc(EntityAr[entPlayer].Ammo[2].Current, 5);
                if EntityAr[entPlayer].Ammo[2].Current>EntityAr[entPlayer].Ammo[2].Maximum then
                  EntityAr[entPlayer].Ammo[2].Current:=EntityAr[entPlayer].Ammo[2].Maximum;
                result:=true;
                PlaySnd(s_AmmoPickUp, 0);
              end;
    itmAmmo+1:begin
                if EntityAr[entPlayer].Ammo[3].Current=EntityAr[entPlayer].Ammo[3].Maximum then exit;
                inc(EntityAr[entPlayer].Ammo[3].Current, 10);
                if EntityAr[entPlayer].Ammo[3].Current>EntityAr[entPlayer].Ammo[3].Maximum then
                  EntityAr[entPlayer].Ammo[3].Current:=EntityAr[entPlayer].Ammo[3].Maximum;
                result:=true;
                PlaySnd(s_AmmoPickUp, 0);
              end;
    itmAmmo+2:begin
                if EntityAr[entPlayer].Ammo[4].Current=EntityAr[entPlayer].Ammo[4].Maximum then exit;
                inc(EntityAr[entPlayer].Ammo[4].Current, 1);
                if EntityAr[entPlayer].Ammo[4].Current>EntityAr[entPlayer].Ammo[4].Maximum then
                  EntityAr[entPlayer].Ammo[4].Current:=EntityAr[entPlayer].Ammo[4].Maximum;
                result:=true;
                PlaySnd(s_AmmoPickUp, 0);
              end;

    itmGuns : begin
                EntityAr[entPlayer].Weapon[2]:=true;
                inc(EntityAr[entPlayer].Ammo[2].Current, 10);
                if EntityAr[entPlayer].Ammo[2].Current>EntityAr[entPlayer].Ammo[2].Maximum then
                  EntityAr[entPlayer].Ammo[2].Current:=EntityAr[entPlayer].Ammo[2].Maximum;
                result:=true;
                PlaySnd(s_ChangeWeapon, 0);
              end;
   itmGuns+1: begin
                EntityAr[entPlayer].Weapon[3]:=true;
                inc(EntityAr[entPlayer].Ammo[3].Current, 20);
                if EntityAr[entPlayer].Ammo[3].Current>EntityAr[entPlayer].Ammo[3].Maximum then
                  EntityAr[entPlayer].Ammo[3].Current:=EntityAr[entPlayer].Ammo[3].Maximum;
                result:=true;
                PlaySnd(s_ChangeWeapon, 0);
              end;
   itmGuns+2: begin
                EntityAr[entPlayer].Weapon[4]:=true;
                inc(EntityAr[entPlayer].Ammo[4].Current, 5);
                if EntityAr[entPlayer].Ammo[4].Current>EntityAr[entPlayer].Ammo[4].Maximum then
                  EntityAr[entPlayer].Ammo[4].Current:=EntityAr[entPlayer].Ammo[4].Maximum;
                result:=true;
                PlaySnd(s_ChangeWeapon, 0);
              end;
   itmInvul:  begin
                BonusInvul:=BonusTime;
                result:=true;
                PlaySnd(s_LifePickUp, 0);
              end;

  end;
  if result then
    Level[CellX, CellY].Item:=0;
end;

//----------------------------------------------------------------------------------------------------------------------

function isNotBlocked (Coord: TPoint; var Level:TLevel):boolean;
begin
  result:=false;
  if not isValidCell(Coord.X, Coord.Y, Level) then exit;
  if Level[Coord.X, Coord.Y].Block in [blWall, blBedRock] then exit;
  if Level[Coord.X, Coord.Y].Entity <> 0 then exit;
  result:=true;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure MoveEntity(EntityIdx:integer; var Entity:TEntity; var Level: TLevel);
begin
  Level[Entity.Coord.X, Entity.Coord.Y].Entity:=0;
  Entity.Coord:=Entity.Planed;
  Level[Entity.Coord.X, Entity.Coord.Y].Entity:=EntityIdx;
end;

//----------------------------------------------------------------------------------------------------------------------

function ScreenToWorld(X, Y: integer):TPoint;
begin
  result.X:=ScrollX+X;
  result.Y:=ScrollY+Y;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure SwitchWeapon(WeaponN:integer);
begin
  if SwithReady<>0 then exit;
  if EntityAr[entPlayer].Weapon[WeaponN] then
  begin
    SwithReady:=SwithDelay;  
    EntityAr[entPlayer].CurrentWeapon:=WeaponN;
    PlaySnd(s_ChangeWeapon, 0);
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure GameControl;
begin
  EntityAr[entPlayer].Target:=ScreenToWorld(GameInput.Mouse.X, GameInput.Mouse.Y);
  if IsControl(keyLMB)  then EntityAr[entPlayer].Shoot:=true;
  if IsControl(keyUp)   then dec(EntityAr[entPlayer].Planed.Y);
  if IsControl(keyDown) then inc(EntityAr[entPlayer].Planed.Y);
  if IsControl(keyLeft) then dec(EntityAr[entPlayer].Planed.X);
  if IsControl(keyRight)then inc(EntityAr[entPlayer].Planed.X);
  if IsControl(key1)    then SwitchWeapon(1);
  if IsControl(key2)    then SwitchWeapon(2);
  if IsControl(key3)    then SwitchWeapon(3);
  if IsControl(key4)    then SwitchWeapon(4);
  if IsControl(keyHelp) then
  begin
      HSBackTo:=gsGame;
      GameState:=gsHelp;
  end;
  if IsControl(keyEscape) then
  begin
      MMBackTo:=gsGame;
      mMainMenu;
      GameState:=gsMenu;
  end;

end;

//----------------------------------------------------------------------------------------------------------------------

function GetStartPoint(x0, y0, x1, y1:integer):TPoint;
var Line: TPointsAr;
begin
  result.X:=0;
  result.Y:=0;
  BLine(x0, y0, x1, y1, Line);
  if High(Line)>=1 then
  begin
    result.X:=Line[1].X;
    result.Y:=Line[1].Y;
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

function FastProjectile(x0, y0, x1, y1:integer; Damage:integer; var Level: Tlevel):TPoint; // Pistol, ShotGun, Machinegun
var Line: TPointsAr;
    a:integer;
    EntityIdx:integer;
begin
  result.X:=0;
  result.Y:=0;
  BLine(x0, y0, x1, y1, Line);
  if High(Line)>=0 then
    for a:=0 to High(Line) do
      if a>0 then // Ingnore cell with shooter
      begin
        if a=1 then
        begin
          result.X:=Line[a].X;
          result.Y:=Line[a].Y;
        end;

        AddFx(Line[a].X, Line[a].Y, ShotLineDelay, colAlpha, colAlpha, chShotLine);
        //Level[Line[a].X, Line[a].Y].FxBlock:=chShotLine;
        //Level[Line[a].X, Line[a].Y].FxFG:=colWhite;
        //Level[Line[a].X, Line[a].Y].FxDelay:=ShotLineDelay;
        if Level[Line[a].X, Line[a].Y].Block in [blWall, blBedrock] then  //Wall hit
        begin
          //Level[Line[a].X, Line[a].Y].FxBlock:=blWall+2;
          //Level[Line[a].X, Line[a].Y].FxFG:=clWallHit;
          //Level[Line[a].X, Line[a].Y].FxDelay:=FxDelay;
          AddFx(Line[a].X, Line[a].Y, FxDelay, clWallHit, colAlpha, blWall+2);
          break;
        end;
        EntityIdx:=Level[Line[a].X, Line[a].Y].Entity;
        if EntityIdx <>0 then // Entity hit
        begin
          EntityAr[EntityIdx].DamageTaken:=EntityAr[EntityIdx].DamageTaken+Damage;
          if a+1 <= High(Line) then
            Level[Line[a+1].X, Line[a+1].Y].Blood:=2;
          if a+2 <= High(Line) then
            Level[Line[a+2].X, Line[a+2].Y].Blood:=2;
          //PlaySnd(s_manhit, 0);
          break;
        end;

      end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure Attack(EntityIdx: integer; var Level: Tlevel);
var dx, dy: integer;
    a:integer;
    FGC:byte;
    CurrentWeapon:integer;
    MuzzleFlash:TPoint;
    Projectile:integer;
    StartPoint:TPoint;
begin
  CurrentWeapon:=EntityAr[EntityIdx].CurrentWeapon;
  EntityAr[EntityIdx].Shoot:=false;
  if EntityAr[EntityIdx].WeaponReady[CurrentWeapon]<>0 then exit;
  a:=1;
  if EntityIdx <> entPlayer then a:=2;
  if EntityAr[EntityIdx].Ammo[CurrentWeapon].Current=0 then
  begin
    PlaySnd(s_DryShot, 0);
    EntityAr[EntityIdx].WeaponReady[CurrentWeapon]:=WeaponRelax[CurrentWeapon];
    exit;
  end;
  if EntityAr[EntityIdx].Ammo[CurrentWeapon].Current>0 then
    dec(EntityAr[EntityIdx].Ammo[CurrentWeapon].Current);
  EntityAr[EntityIdx].WeaponReady[CurrentWeapon]:=WeaponRelax[CurrentWeapon] * a;
  PlaySnd(WeaponSound[CurrentWeapon], 0);
  dx:=EntityAr[EntityIdx].Target.X;
  dy:=EntityAr[EntityIdx].Target.Y;

  Projectile:=1;
  if CurrentWeapon in [1, 2, 3] then
  begin
    case CurrentWeapon of
      1: Projectile:=1;
      2: Projectile:=5;
      3: Projectile:=1;
    end;
    for a:=1 to Projectile do
    begin
      Trajectory(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, dx, dy, WeaponDeviation[CurrentWeapon] * a);
      MuzzleFlash:=FastProjectile(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, dx, dy, WeaponDamage[CurrentWeapon], Level);
      if random(2)=0 then FGC:=colLightYellow else FGC:=colLightRed;
      EntityAr[EntityIdx].MuzzleFlash.Coord.X:=MuzzleFlash.X-EntityAr[EntityIdx].Coord.X;
      EntityAr[EntityIdx].MuzzleFlash.Coord.Y:=MuzzleFlash.Y-EntityAr[EntityIdx].Coord.Y;
      EntityAr[EntityIdx].MuzzleFlash.FG:=FGC;
      EntityAr[EntityIdx].MuzzleFlash.Ch:=chStar8;
      EntityAr[EntityIdx].MuzzleFlash.Delay:=FxDelay;
    end;
  end;

  if CurrentWeapon in [4] then
  begin
    StartPoint:=GetStartPoint(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, dx, dy);
    AddRocket(StartPoint.X, StartPoint.Y, dX, dY, 100, 50, colLightRed);
  end;

  if CurrentWeapon in [5] then
  begin
    StartPoint:=GetStartPoint(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, dx, dy);
    a:=level[StartPoint.X, StartPoint.Y].Entity;
    if a <> 0 then
      EntityAr[a].DamageTaken:=EntityAr[a].DamageTaken + WeaponDamage[CurrentWeapon];
  end;

  //MuzzleFlash:=

//
//  Ang:=GetAngle(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, EntityAr[EntityIdx].Target.X, EntityAr[EntityIdx].Target.Y);
//  dx:=trunc(AimDist*sin(Ang*pi180));
//  dy:=trunc(AimDist*cos(Ang*pi180));
//  dx:=EntityAr[EntityIdx].Coord.X+dx;
//  dy:=EntityAr[EntityIdx].Coord.Y-dy;
//
//  BLine(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, dx, dy, Line);
//  if High(Line)>=0 then
//    for a:=0 to High(Line) do
//    begin
//      if a=1 then
//      begin
//        if random(2)=0 then FGC:=colLightYellow else FGC:=colLightRed;
//        EntityAr[EntityIdx].MuzzleFlash.Coord.X:=Line[a].X-EntityAr[EntityIdx].Coord.X;
//        EntityAr[EntityIdx].MuzzleFlash.Coord.Y:=Line[a].Y-EntityAr[EntityIdx].Coord.Y;
//        EntityAr[EntityIdx].MuzzleFlash.FG:=FGC;
//        EntityAr[EntityIdx].MuzzleFlash.Ch:=chStar8;
//        EntityAr[EntityIdx].MuzzleFlash.Delay:=FxDelay;
//        //dx:=PlayerFx.Coord.X;
//        //dy:=PlayerFx.Coord.Y;
//        //wnd.Caption(pchar('ASCII Caverns ['+inttostr(dx)+':'+inttostr(dy)+' '+inttostr(PlayerFx.Delay)+']'));
//      end;
//      if Level[Line[a].X, Line[a].Y].Block in [blWall, blBedrock] then
//      begin
//        Level[Line[a].X, Line[a].Y].FxBlock:=blWall+2;
//        Level[Line[a].X, Line[a].Y].FxFG:=colRed;
//        Level[Line[a].X, Line[a].Y].FxDelay:=FxDelay;
//        break;
//      end;
//    end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AI(EntityIdx:integer);
var Px,Py:integer;
    Line: TPointsAr;
    D:integer;
begin
  if EntityIdx<=entPlayer then exit;
  Px:= EntityAr[entPlayer].Coord.X;
  Py:= EntityAr[entPlayer].Coord.Y;
  BLine(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, Px, Py, Line);
  D:=High(Line);
  if D<=EntityAr[EntityIdx].ViewRange.Maximum then
    begin
      if D<=EntityAr[EntityIdx].ViewRange.Current then
      begin
        if random(20)=0 then
        begin
          if isNotBlocked (Line[1], CurrentLevel) then
            EntityAr[EntityIdx].Planed:=Line[1]
        end
        else
        begin
          GetAround(EntityAr[entPlayer].Coord.X, EntityAr[entPlayer].Coord.Y, CurrentLevel, Line, false);
          D:=random(High(Line)+1);
          EntityAr[EntityIdx].Target:=Line[D];
          EntityAr[EntityIdx].Shoot:=isVisibleCell(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, Px, Py, CurrentLevel);
        end;
      end
      else
      begin
        if isNotBlocked (Line[1], CurrentLevel) then
          EntityAr[EntityIdx].Planed:=Line[1]
        else
        begin
          GetAround(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y, CurrentLevel, Line);
          if High(Line)>=0 then
            EntityAr[EntityIdx].Planed:=Line[random(High(Line)+1)];
        end;
      end;
    end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure ProcessEntityAr (var Level:TLevel);
var EntityIdx, a:integer;
    Def:single;
    ArmorDam:single;
    HealthDam:single;
    DST:single;
begin
  for EntityIdx:=1 to High(EntityAr) do
  begin
    if EntityAr[EntityIdx].HP.Current<=0 then continue;
    for a:=1 to High(EntityAr[EntityIdx].WeaponReady) do
      if EntityAr[EntityIdx].WeaponReady[a]>0 then EntityAr[EntityIdx].WeaponReady[a]:= EntityAr[EntityIdx].WeaponReady[a]-1;

    AI(EntityIdx);
    if EntityAr[EntityIdx].MoveDelay>0 then
      dec(EntityAr[EntityIdx].MoveDelay);

    if EntityIdx=entPlayer then
      if not CoordEqual(EntityAr[EntityIdx].Coord, EntityAr[EntityIdx].Planed) then
        if isNotBlocked (EntityAr[EntityIdx].Planed, Level) then
        begin
          MoveEntity(EntityIdx, EntityAr[EntityIdx], Level);
          if CoordEqual(Portal.Coord,EntityAr[EntityIdx].Coord) then
            Portal.State:=2;
        end
        else
          EntityAr[EntityIdx].Planed:=EntityAr[EntityIdx].Coord;

    if (EntityIdx<>entPlayer) and (EntityAr[EntityIdx].MoveDelay=0) then
      if not CoordEqual(EntityAr[EntityIdx].Coord, EntityAr[EntityIdx].Planed) then
        begin
          if isNotBlocked (EntityAr[EntityIdx].Planed, Level) then
            MoveEntity(EntityIdx, EntityAr[EntityIdx], Level)
          else
            EntityAr[EntityIdx].Planed:=EntityAr[EntityIdx].Coord;
          EntityAr[EntityIdx].MoveDelay:=enMoveDelay;
        end;

    if EntityAr[EntityIdx].Shout>0 then
      dec(EntityAr[EntityIdx].Shout);

    if (EntityAr[EntityIdx].DamageTaken>0) and (EntityIdx=entPlayer) and (BonusInvul>0) then
      EntityAr[EntityIdx].DamageTaken:=0;

    if (EntityAr[EntityIdx].DamageTaken>0) {and (EntityIdx>entPlayer)} then
      begin
        if EntityAr[EntityIdx].Shout=0 then
        begin
          DST:=GetDist(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y);
          PlaySnd(s_manhit, DST);
          EntityAr[EntityIdx].Shout:=10;
        end;
        a:=1;
        if EntityIdx = entPlayer then a:=2;
        Def:=EntityAr[EntityIdx].Armor.Current;
        if Def>100 then Def:=100;
        ArmorDam:=EntityAr[EntityIdx].DamageTaken/100 * Def * a;
        HealthDam:=EntityAr[EntityIdx].DamageTaken - ArmorDam;
        if HealthDam < 0 then HealthDam:=0;
        Def:=Def-ArmorDam/a;
        if Def<0 then Def:=0;
        EntityAr[EntityIdx].Armor.Current:=trunc(Def);
        EntityAr[EntityIdx].HP.Current:=trunc(EntityAr[EntityIdx].HP.Current - HealthDam);

        if EntityAr[EntityIdx].HP.Current<=0 then
        begin
          Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Entity:=0;
          EntityAr[EntityIdx].HP.Current:=0;

          if EntityIdx=entPlayer then Portal.State:=100;
          Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Blood:=chCorpse;
          a:=random(100);
          if a<50 then
            if EntityAr[EntityIdx].TypeOf in [1,2,4] then
              if Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item=0 then
                if a<10 then
                  case EntityAr[EntityIdx].TypeOf of
                    1: Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item:=itmGuns+1;
                    2: Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item:=itmGuns;
                    4: Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item:=itmGuns+2;
                  end
                else
                  case EntityAr[EntityIdx].TypeOf of
                    1: Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item:=itmAmmo+1;
                    2: Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item:=itmAmmo;
                    4: Level[EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y].Item:=itmAmmo+2;
                  end;
        end;

        EntityAr[EntityIdx].DamageTaken:=0;
        if EntityIdx=entPlayer then
          AddFx(0, 0, 3, colAlpha, colRed, 0);
        //DST:=GetDist(EntityAr[EntityIdx].Coord.X, EntityAr[EntityIdx].Coord.Y);

        //if EntityAr[EntityIdx].Shout=0 then PlaySnd(s_ManHit, DST);
         //  EntityAr[EntityIdx].Shout:=0;
      end;

    if EntityIdx = entPlayer then
    begin
      if Level[EntityAr[EntityIdx].Coord.X,   EntityAr[EntityIdx].Coord.Y-1].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X,   EntityAr[EntityIdx].Coord.Y-1, Level);
      if Level[EntityAr[EntityIdx].Coord.X+1, EntityAr[EntityIdx].Coord.Y-1].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X+1, EntityAr[EntityIdx].Coord.Y-1, Level);
      if Level[EntityAr[EntityIdx].Coord.X+1, EntityAr[EntityIdx].Coord.Y  ].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X+1, EntityAr[EntityIdx].Coord.Y,   Level);
      if Level[EntityAr[EntityIdx].Coord.X+1, EntityAr[EntityIdx].Coord.Y+1].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X+1, EntityAr[EntityIdx].Coord.Y+1, Level);
      if Level[EntityAr[EntityIdx].Coord.X  , EntityAr[EntityIdx].Coord.Y+1].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X,   EntityAr[EntityIdx].Coord.Y+1, Level);
      if Level[EntityAr[EntityIdx].Coord.X-1, EntityAr[EntityIdx].Coord.Y+1].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X-1, EntityAr[EntityIdx].Coord.Y+1, Level);
      if Level[EntityAr[EntityIdx].Coord.X-1, EntityAr[EntityIdx].Coord.Y  ].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X-1, EntityAr[EntityIdx].Coord.Y,   Level);
      if Level[EntityAr[EntityIdx].Coord.X-1, EntityAr[EntityIdx].Coord.Y-1].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X-1, EntityAr[EntityIdx].Coord.Y-1, Level);
      if Level[EntityAr[EntityIdx].Coord.X,   EntityAr[EntityIdx].Coord.Y  ].Item <> 0 then PickUpItem(EntityAr[EntityIdx].Coord.X,   EntityAr[EntityIdx].Coord.Y,   Level);
    end;
    if EntityAr[EntityIdx].Shoot then Attack(EntityIdx, Level);
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure DrawWeaponData(GUI_X, GUI_Y:integer);
var TempColor:byte;
    dx:integer;
begin
  PrintString('1: Пистолет', GUI_X+1, GUI_Y, ColorFilter(colLightGray, CFilter));

  TempColor:=IfThenElseColor(EntityAr[entPlayer].Weapon[2], colLightGray, colDarkGray);
  PrintString('2: Дробовик', GUI_X+1, GUI_Y+1, ColorFilter(TempColor, CFilter));

  TempColor:=IfThenElseColor(EntityAr[entPlayer].Weapon[3], colLightGray, colDarkGray);
  PrintString('3: Автомат', GUI_X+1, GUI_Y+2, ColorFilter(TempColor, CFilter), colBlack);

  TempColor:=IfThenElseColor(EntityAr[entPlayer].Weapon[4], colLightGray, colDarkGray);
  PrintString('4: Ракетомёт', GUI_X+1, GUI_Y+3, ColorFilter(TempColor, CFilter), colBlack);

  dx:=13;
  PrintString('[+++]', GUI_X+dx, GUI_Y, ColorFilter(colWhite, CFilter));

  TempColor:=IfThenElseColor(EntityAr[entPlayer].Ammo[2].Current>0, colWhite, colDarkGray);
  PrintString(format('[%3.3d]',[EntityAr[entPlayer].Ammo[2].Current]), GUI_X+dx, GUI_Y+1, ColorFilter(TempColor, CFilter));

  TempColor:=IfThenElseColor(EntityAr[entPlayer].Ammo[3].Current>0, colWhite, colDarkGray);
  PrintString(format('[%3.3d]',[EntityAr[entPlayer].Ammo[3].Current]), GUI_X+dx, GUI_Y+2, ColorFilter(TempColor, CFilter), colBlack);

  TempColor:=IfThenElseColor(EntityAr[entPlayer].Ammo[4].Current>0, colWhite, colDarkGray);
  PrintString(format('[%3.2d]',[EntityAr[entPlayer].Ammo[4].Current]), GUI_X+dx, GUI_Y+3, ColorFilter(TempColor, CFilter), colBlack);

  PrintString('<', GUI_X, GUI_Y+EntityAr[entPlayer].CurrentWeapon-1, ColorFilter(colWhite, CFilter), colBlack);
  PrintString('>', GUI_X+18, GUI_Y+EntityAr[entPlayer].CurrentWeapon-1, ColorFilter(colWhite, CFilter), colBlack);

  //FillBG(GUI_X, GUI_Y, 16, colBlue);
  //wnd.Caption(pchar('ASCII Caverns ['+floattostr(EntityAr[1].DamageTaken)+']'))
end;

//----------------------------------------------------------------------------------------------------------------------

procedure DrawPlayerData(GUI_X, GUI_Y:integer);
var s:string;
begin
  s:='%Life%';
  s:=ReplaceSymbol(s);
  PrintString('Жизнь', GUI_X, GUI_Y, ColorFilter(colLightGray, CFilter));
  PrintString(s, GUI_X+5, GUI_Y, ColorFilter(colLightRed, CFilter));
  PrintString(format('%3.3d%s',[EntityAr[entPlayer].HP.Current, '%']), GUI_X+6, GUI_Y, ColorFilter(colWhite, CFilter));

  s:='%Armor%';
  s:=ReplaceSymbol(s);
  PrintString('Броня', GUI_X, GUI_Y+1, ColorFilter(colLightGray, CFilter));
  PrintString(s, GUI_X+5, GUI_Y+1, ColorFilter(colLightBlue, CFilter));
  PrintString(format('%3.3d%s',[EntityAr[entPlayer].Armor.Current,'%']), GUI_X+6, GUI_Y+1, ColorFilter(colWhite, CFilter));

  PrintString('Уровень:', GUI_X, GUI_Y+3, ColorFilter(colLightGray, CFilter));
  PrintString(format('%2.2d%',[LevelN,'%']), GUI_X+8, GUI_Y+3, ColorFilter(colWhite, CFilter));
end;

//----------------------------------------------------------------------------------------------------------------------

procedure GenerateItems(DifficultLevel:integer; Level:Tlevel);
var Idx:integer;
    Target:TPoint;
    s:string;
    Bonus:integer;
begin

  for Idx:=1 to enTotal div 2 do
  begin
    Target:=DeployItems(3, Level);
    Level[Target.X, Target.Y].Item:=itmLife;
  end;

  for Idx:=1 to enTotal div 2 do
  begin
    Target:=DeployItems(3, Level);
    Level[Target.X, Target.Y].Item:=itmArmor;
  end;

  s:='';
  if LevelN >= wpLevelSG-1 then
    s:=s+'0000';
  if LevelN >= wpLevelMG-1 then
    s:=s+'1111';
  if LevelN >= wpLevelRL-1 then
    s:=s+'22';
  for Idx:=1 to 80 do
  begin
    Bonus:=strtoint(s[random(length(s))+1])+itmAmmo-1;
    Target:=DeployItems(2, Level);
    Level[Target.X, Target.Y].Item:=Bonus;
  end;

  s:='';
  if LevelN >= wpLevelSG then
    s:=s+'0000';
  if LevelN >= wpLevelMG then
    s:=s+'111';
  if LevelN >= wpLevelRL then
    s:=s+'2';
  if s<>'' then
    for Idx:=1 to 10 do
    begin
      Bonus:=strtoint(s[random(length(s))+1])+itmGuns;
      Target:=DeployItems(1, Level);
      Level[Target.X, Target.Y].Item:=Bonus;
    end;

  Target:=DeployItemsEmp(Level);
  Level[Target.X, Target.Y].Item:=itmInvul;
  Target:=DeployItemsEmp(Level);
  Level[Target.X, Target.Y].Item:=itmInvul;

end;

//----------------------------------------------------------------------------------------------------------------------

procedure PrepareNewLevel(LevelN: integer; var Level:TLevel);
var w,h:integer;
begin
  SetLength(Level,0);
  w:=16;
  h:=16;
  randseed:=seed;
  CreateLevel(w, h, 3, 2, '00000011111', CurrentLevel);
  InitEnemy;
  InitEntityAr;
  Portal.Coord:=FoundEnd(CurrentLevel);
  Portal.State:=0;
  GenerateItems(GameDifficult, CurrentLevel);
  BonusInvul:=0;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure DrawGame;
var h:integer;
begin
  if isFirstTimeGame then
  begin
    isFirstTimeGame:=false;
    seed:=0;//random(10000);
//    w:=32;
//    h:=32;
//    CreateLevel(w, h, 3, 2, '00000011111', CurrentLevel);
//    //SaveArr('Test', CurrentLevel);
    InitPlayer;
//    InitEnemy;
//    InitEntityAr;
//    //InitEntityAr;
//    GenerateItems(GameDifficult, CurrentLevel);
    LevelN:=1;
    PrepareNewLevel(1, CurrentLevel);
  end;


  GameControl;

  ProcessProjectileAr(CurrentLevel);
  ProcessFx;


  ScrollX:=EntityAr[entPlayer].Coord.X - (ViewW-1) div 2;
  ScrollY:=EntityAr[entPlayer].Coord.Y - (ViewH-1) div 2;
  ShowLevel(0, 0, ViewW, ViewH, ScrollX, ScrollY, EntityAr[entPlayer].Coord.X, EntityAr[entPlayer].Coord.Y, CFilter, CurrentLevel);

  for h:=0 to ViewH-1 do
    PrintString('|', ViewW, h, ColorFilter(colDarkGray, CFilter), colBlack);

  DrawWeaponData(ViewW+2, 20);

  DrawPlayerData(ViewW+23, 20);

  if BonusInvul>0 then
  begin
    CFilter:=colLightYellow;
    PrintString(format('00:%2.2d',[BonusInvul div 60]), 20, 5, ColorFilter(colLightYellow, CFilter));
  end
  else
    CFilter:=colAlpha;


  ProcessEntityAr(CurrentLevel);

  if SwithReady>0 then dec(SwithReady);
  if BonusInvul>0 then dec(BonusInvul);

  if Portal.State>3 then
    dec(Portal.State)
  else
  begin
    if Portal.State=2 then
      begin
        PlaySnd(s_PickUpArmor, 0);
        _Player:=EntityAr[entPlayer];
        inc(LevelN);
        seed:=random(10000);
        PrepareNewLevel(LevelN, CurrentLevel);
        ISType:=0;
        GameState:=gsInfo;
      end;
    if Portal.State=3 then
      begin
        PlaySnd(s_manhit, 0);
        InitPlayer;
        PrepareNewLevel(LevelN, CurrentLevel);
        ISType:=1;
        GameState:=gsInfo;
      end;
  end;

end;


//----------------------------------------------------------------------------------------------------------------------

end.



