unit LevelUnit;

{$mode delphi}

interface

uses
  Classes,
  SysUtils,
  sceneUnit,
  zglHeader,
  GameUnits,
  var_const,
  enemy_unit,
  zglGui,
  units_panel;

type

  { TLevelScene }




  TLevelScene = class(TScene)
  protected
    Enemys:array of TEnemy;
  //  UpdatingList: array of integer;           //Update LIst for CPU

    levCamera: zglTCamera2d;
    levMap: array[1..32, 1..32] of byte;

    panelGUI:zglTGui;
    gamestarted:boolean;
    win:boolean;
    procedure Add_Sentry(type_:byte;X_,Y_:Single);
  public
    selectedUnit: integer;
    PlayerTurn:boolean;
    movesLeft:byte;
    RepairsLeft:byte;
    score:integer;
   // cpuRepairsLeft:byte;
    //cpuBuildingsLeft:byte;
   // cpuAirstrikesLeft:byte;

    takenUnit:integer;
    Wariors: array of TGameUnit;
    procedure LoadLevel(levID:byte);
    procedure Draw; override;
    procedure Proc(dt: double); override;
    procedure Init; override;
    procedure Exit; override;
    function CellIsFree(X, Y: single;Barikads:boolean = true;CheckWariors:boolean = false;CheckEnemys:boolean = false): boolean;

    function GetWariorCell(X,Y:Single):pointer;
    function GetEnemyCell(X,Y:Single):pointer;

    function RayTrace(ray:zglPLine):boolean;
  end;

implementation

{ TLevelScene }

procedure TLevelScene.Add_Sentry(type_: byte; X_, Y_: Single);
begin
    SetLength(Enemys,Length(Enemys)+1);
    Enemys[Length(Enemys)-1]:=TEnemy.Create(type_,X_,Y_);
end;



procedure TLevelScene.Draw;
var
  i, j: integer;

begin
 batch2d_Begin();
 for i := 0 to (Game.GetScreenH div 16)+1  do
   for j := 0 to (Game.GetScreenW div 16)+1 do
   begin
     ssprite2d_draw(bgTex, j * 16, i * 16, 16, 16, 0);
   end;
  cam2d_set(@levCamera);
  // inherited Draw;
  //pr2d_Rect(0, 0, levSize, levSize, $00FF00,255,pr2d_FILL);

  for i := 1 to 32 do
    for j := 1 to 32 do
    begin
      ssprite2d_draw(floor, (j - 1) * 32, (i - 1) * 32, 32, 32, 0);
    end;

  if length(wariors) <> 0 then
    for i := 0 to length(wariors) - 1 do
      wariors[i].Draw;

  If length(enemys) <> 0 then
    for i:=0 to length(enemys) - 1 do
      enemys[i].Draw;

  for i := 1 to 32 do
    for j := 1 to 32 do
      case levMap[j, i] of
        1: ssprite2d_draw(walltex_0, (j - 1) * 32 + 4, (i - 1) * 32 + 4, 32, 32, 0);
        2: ssprite2d_draw(walltex_1, (j - 1) * 32 + 4, (i - 1) * 32 + 4, 32, 32, 0);
        3: pr2d_rect((j - 1) * 32 + 4, (i - 1) * 32 + 4, 32, 32, $AA0000, 32, PR2D_FILL);
      end;
  ssprite2d_draw(tracerTex, (round(MOUSE_X / levCamera.Zoom.X + levCamera.X) div 32)*32,
  (round(MOUSE_Y/ levCamera.Zoom.Y + levCamera.Y) div 32)*32, 32, 32, 0,100);
  if length(enemys)>0 then
  for i:=0 to length(enemys)-1 do
   if ((round(MOUSE_X / levCamera.Zoom.X + levCamera.X) div 32)+1=enemys[i].X)and
      ((round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y) div 32)+1=enemys[i].Y) then
      begin
        pr2d_rect(enemys[i].X *32-32,enemys[i].Y *32-32,32,10,$000000,200,PR2D_FILL);
        pr2d_rect(enemys[i].X *32-32+2,enemys[i].Y *32-32+2,(32-4)*enemys[i].Health/enemys[i].StartingHealth,6,$00EE00,100,PR2D_FILL);
      end;
  Pengine2d_Set(@Particles);
  Pengine2d_Draw();
  Pengine2d_Set(@Particles1);
  Pengine2d_Draw();
  cam2d_set(nil);
  batch2d_End();
  panelGui.Draw;
  panelGUI.DrawMouse;
end;

procedure TLevelScene.Proc(dt: double);
var randomList:array of integer;
      i,j: integer;
      tempcircle: zglTCircle;
      randomUnit:integer;
      stillShooting:boolean;
      tempPointer:pointer;
      minRange:single;
      minRangeID:integer;
begin
  stillShooting:=false;
  for i:=0 to length(enemys)-1 do
   stillShooting:=stillShooting or enemys[i].shooting;
 if (movesLeft=0)and(not stillShooting) then
   begin
    PlayerTurn:=not PlayerTurn;
    if PlayerTurn then inc(score);
    movesLeft:=2;
   // sleep(50);
   // setLength(UpdatingList,0);
   end;



  for i:=0 to length(enemys)-1 do
     if enemys[i].isKilled then
       begin
         inc(score);
         enemys[i].Kill;
         enemys[i].Free;
         if i<> length(enemys)-1 then

         for j:=i to length(enemys)-2 do
           enemys[j]:=enemys[j+1];
         setLength(enemys,length(enemys)-1);
         break;
       end;

  for i:=0 to length(wariors)-1 do
     if wariors[i].isKilled then
       begin
         wariors[i].Kill;
         wariors[i].Free;
         if i<> length(wariors)-1 then

         for j:=i to length(wariors)-2 do
           wariors[j]:=wariors[j+1];
         setLength(wariors,length(wariors)-1);
         selectedUnit:=-9999;
         break;
       end;

  if PlayerTurn then
    begin
  if ((takenUnit=Unit_Soldier)and(SoldierLabel.Caption='0'))or
     ((takenUnit=Unit_RocketMan)and(RocketManLabel.Caption='0'))or
     ((takenUnit=Unit_Tank)and(TankLabel.Caption='0'))or                  //Если юнитов не осталось.
     ((takenUnit=Unit_Medic)and(MedicLabel.Caption='0'))or
     ((takenUnit=Unit_Capitan)and(CapitanLabel.Caption='0'))or
     ((takenUnit=Unit_Sniper)and(SniperLabel.Caption='0')) then
       takenUnit:=-9999;
  If TakenUnit>=0 then selectedUnit:=-9999;

 if (MOUSE_X / levCamera.Zoom.X + levCamera.X>=0)and(MOUSE_X / levCamera.Zoom.X + levCamera.X<=1023)and
    (MOUSE_Y / levCamera.Zoom.Y + levCamera.Y>=0)and(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y<=1023) then
      begin
      if selectedUnit>=0 then PanelGUI.Mouse:=4 else
       if takenUnit>=0 then PanelGUI.Mouse:=3 else
        PanelGUI.Mouse:=1;
      end else PanelGUI.Mouse:=1;

  if selectedUnit >= 0 then
  begin
    if Wariors[selectedUnit].isAbleToMove(
      round(MOUSE_X / levCamera.Zoom.X + levCamera.X) div 32 + 1,
      round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y) div 32 + 1) then
      if MOUSE_CLICK(M_BRIGHT) then
        Wariors[selectedUnit].addWayPoint(
          round(MOUSE_X / levCamera.Zoom.X + levCamera.X) div 32 + 1,
          round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y) div 32 + 1);
  end;


  if (MOUSE_X / levCamera.Zoom.X + levCamera.X>=0)and(MOUSE_X / levCamera.Zoom.X + levCamera.X<=1023)and
      (MOUSE_Y / levCamera.Zoom.Y + levCamera.Y>=0)and(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y<=1023)then
  if mouse_click(M_BLEFT) then
  begin
    if selectedUnit >= 0 then
    begin
     if wariors[selectedUnit].class_<>Unit_Medic then
      wariors[selectedunit].Shoot(round(MOUSE_X / levCamera.Zoom.X + levCamera.X),
        round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y)) else
         begin
          tempPointer:=nil;
           for i:=0 to length(wariors)-1 do
             if (round(MOUSE_X / levCamera.Zoom.X + levCamera.X)div 32=round(wariors[i].Pos.X) div 32) and
           (round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y)div 32=round(wariors[i].Pos.Y) div 32) then
           begin
             tempPointer:=wariors[i];
             break;
           end;
         wariors[selectedUnit].tryToHeal(tempPointer);
        end;
    end
    else
     if takenUnit>=0 then
      if levMap[round(MOUSE_X / levCamera.Zoom.X + levCamera.X) div 32 + 1][round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y) div 32 + 1]=3 then
    begin
      setLength(Wariors, Length(Wariors) + 1);
      Wariors[Length(Wariors) - 1] :=
        TGameUnit.Create(takenUnit, round(MOUSE_X / levCamera.Zoom.X + levCamera.X) div
        32 + 1, round(MOUSE_Y / levCamera.Zoom.Y + levCamera.Y) div 32 + 1, False);
      case takenUnit of
       Unit_Soldier:   SoldierLabel.Caption:=inttostr(strtoint(SoldierLabel.Caption)-1);
       Unit_Rocketman: RocketmanLabel.Caption:=inttostr(strtoint(RocketmanLabel.Caption)-1);
       Unit_Tank:      TankLabel.Caption:=inttostr(strtoint(TankLabel.Caption)-1);
       Unit_Medic:     MedicLabel.Caption:=inttostr(strtoint(MedicLabel.Caption)-1);
       Unit_Capitan:   CapitanLabel.Caption:=inttostr(strtoint(CapitanLabel.Caption)-1);
       Unit_Sniper:    SniperLabel.Caption:=inttostr(strtoint(SniperLabel.Caption)-1);
      end;
      takenUnit:=-9999;
      snd_Play(MissSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
      gameStarted:=true;
    end;
   end;



  if mouse_click(M_BRIGHT) then
  begin
    TakenUnit:=-9999;
    for i := 0 to length(wariors) - 1 do
      wariors[i].deselect;
    for i := 0 to length(wariors) - 1 do
    begin
      tempcircle.cX := wariors[i].Pos.X;
      tempCircle.cY := wariors[i].Pos.Y;
      tempCircle.Radius := wariors[i].size / 2;
      if col2d_pointInCircle(MOUSE_X / levCamera.Zoom.X + levCamera.X,
        MOUSE_Y / levCamera.Zoom.Y + levCamera.Y, tempcircle) then
      begin
        wariors[i].select;
        selectedUnit := i;
        snd_Play(MissSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
        break;
      end
      else
        selectedUnit := -9999;
    end;
  end;
end else
begin

  if Length(enemys)>0 then

  begin
    if movesleft>0 then
     begin
           Log.Clear;
           setLength(randomList,0);
           for i:=0 to length(enemys)-1 do
            if enemys[i].isAbleToShoot>=0 then
             begin
               setLength(randomList,Length(RandomList)+1);
               randomList[length(randomlist)-1]:=i;
             end;
           if length(randomList)>0 then
            begin
              minRange:=9999;
              minRangeID:=-9999;
              for i:= 0 to Length(randomList)-1 do
               begin
                 if minRange>m_distance(enemys[randomList[i]].X*32-16,enemys[randomList[i]].Y*32-16,
                 wariors[enemys[randomList[i]].isAbleToShoot].Pos.X, wariors[enemys[randomList[i]].isAbleToShoot].Pos.Y)then
                 begin
                 minRange:=m_distance(enemys[randomList[i]].X*32-16,enemys[randomList[i]].Y*32-16,
                 wariors[enemys[randomList[i]].isAbleToShoot].Pos.X, wariors[enemys[randomList[i]].isAbleToShoot].Pos.Y);
                 minRangeID:=i;
                 end;
               end;
              if minRangeID>=0 then
              if enemys[minRangeID].TryToShoot=true then
               Log.AddItem(zglTListItem.Create(Log, 'CPU  is shooting.', 16));
           end;

           if RepairsLeft>=1 then
           for i:=0 to length(enemys)-1 do
            begin
              if enemys[i].Health<=enemys[i].startingHealth/2 then
                begin
                  enemys[i].repair;
                  Log.AddItem(zglTListItem.Create(Log, 'CPU repais self.', 16));
                  break;
                end;
            end;

           while movesLeft>0  do
             begin
               randomUnit:=random(length(enemys));
               enemys[randomUnit].move;
               Log.AddItem(zglTListItem.Create(Log, 'CPU is moving.', 16));
             end;
     end;

  end;



end;

  if Length(enemys)>0 then
   for i:=0 to length(enemys)-1 do
   begin
    enemys[i].Update(dt);
   end;

  if length(wariors) > 0 then
      for i := 0 to length(wariors) - 1 do
        wariors[i].update(dt);


 MOvesLeftLabel.Caption:=inttostr(movesLeft);
 RepairsLabel.Caption:=inttoStr(RepairsLeft);
 ScoreLabel.Caption:=inttostr(score);
 panelGUI.Update(dt);
 Pengine2d_Set(@Particles);
 Pengine2d_proc(dt);
 Pengine2d_Set(@Particles1);
 Pengine2d_Proc(dt);

if (length(enemys)=0)and(not win) then
begin
winScoreLabel.Caption:='Score: '+inttostr(score);
winform.Show;
winForm.MoveToCenter;
win:=true;
if score>bestScore then bestScore:=score;
Pengine2d_Set(@Particles);
Pengine2d_ClearAll;
Pengine2d_Set(@Particles1);
Pengine2d_ClearAll;
end;

if (length(wariors)=0)and(not win)and(gameStarted)and
   (soldierLabel.Caption='0')and(RocketManLabel.Caption='0')and
   (TankLabel.Caption='0')and(MedicLabel.Caption='0')and
   (CapitanLabel.Caption='0')and(SniperLabel.Caption='0')then
begin
failScoreLabel.Caption:='Score: '+inttostr(score);
failform.Show;
failForm.MoveToCenter;
win:=true;
if score>bestScore then bestScore:=score;
Pengine2d_Set(@Particles);
Pengine2d_ClearAll;
Pengine2d_Set(@Particles1);
Pengine2d_ClearAll;

end;


 if Key_Press(K_ESCAPE) then
 begin
   Game.PauseScene('LevelScene');
   Game.DelScene('LevelScene');
   Game.PlayScene('MenuScene');
   Pengine2d_Set(@Particles);
   Pengine2d_ClearAll;
   Pengine2d_Set(@Particles1);
   Pengine2d_ClearAll;
 end;
  mouse_clearState;
  key_clearstate;
end;

procedure TLevelScene.LoadLevel(levID: byte);
var
  MapFile: TStringList;
  i, j: integer;
begin
MOvesLeft:=2;
RepairsLeft:=5;
score:=0;
PlayerTurn:=true;

SelectedUnit := -9999;
takenUnit    := -9999;

SoldierLabel.Caption:='2';
RocketManLabel.Caption:='1';
TankLabel.Caption:='1';
MedicLabel.Caption:='1';
CapitanLabel.Caption:='1';
SniperLabel.Caption:='1';

MapFile := TStringList.Create;
if levID=1 then
MapFile.LoadFromFile('data/maps/0.map');
if levID=2 then
 begin
  MapFile.LoadFromFile('data/maps/1.map');
  TankLabel.Caption:='2';
  SoldierLabel.Caption:='3';
 end;
if levID=3 then
begin
MapFile.LoadFromFile('data/maps/2.map');
 RocketManLabel.Caption:='2';
 TankLabel.Caption:='2';
 MedicLabel.Caption:='0';
 SniperLabel.Caption:='0';
end;



for i := 1 to 32 do
  for j := 1 to 32 do
   if StrToInt(MapFile.Strings[i - 1][j]) < Sentry_normal then   {< range of sentry's}
    levMap[j, i] := StrToInt(MapFile.Strings[i - 1][j]) else
      case StrToInt(MapFile.Strings[i - 1][j]) of
        Sentry_normal:Add_Sentry(Sentry_Normal,j,i);
        Sentry_Rocket:Add_Sentry(Sentry_Rocket,j,i);
        Sentry_Sniper:Add_Sentry(Sentry_Sniper,j,i);
      end;

MapFile.Free;
end;


procedure TLevelScene.Init;
begin

PanelGui := zglTGui.Create(gSkin, 0, 0, Game.GetScreenW,Game.GetScreenH,
  zglTFontContainer.Create(
    zglTFontObject.Create(fntMain, 1, $000000, 255),
    zglTFontObject.Create(fntMain, 1, $101010, 255),
    zglTFontObject.Create(fntMain, 1, $555555, 128)
  ));

 InitGUI(panelGUI);

 with  PanelForm do
 begin
     Rect.H:=Game.GetScreenH;
 end;
  // inherited Init;
  cam2d_init(levCamera);
  levCamera.Zoom.X := Game.GetScreenH / LevSize;
  levCamera.Zoom.Y := levCamera.Zoom.X;
  levCamera.X := -  ((PanelForm.Rect.W+((Game.GetScreenW-PanelForm.Rect.W)/2)-((1024*levCamera.Zoom.X)/2)) / levCamera.Zoom.X);    //<- move it like that
  levCamera.Y := 0;
  levCamera.Center.X := 0;
  levCamera.Center.Y := 0;
  //levCamera.Center;
  pengine2d_Set( @particles );
end;

procedure TLevelScene.Exit;
begin
  // inherited Exit;
//if panelGUI<>nil then
  //panelGUI.ClearModal;
  //panelGUI.Free;
 // tex_del(floor);
  //tex_del(walltex_0); // ADD OTHERS!
end;

function TLevelScene.CellIsFree(X, Y: single;Barikads:boolean = true;CheckWariors:boolean = false;CheckEnemys:boolean = false): boolean;
var
  i: integer;
  useBarikad:integer;
begin
 if Barikads=false then useBarikad:=99 else useBarikad:=2;
  if (levMap[round(x), round(y)] <> 1) and (levMap[round(x), round(y)] <> UseBarikad) and
    (X > 0) and (X < 33) and (Y > 0) and (Y < 33) then
    Result := True
  else
    Result := False;
  if CheckWariors then  result:=result and (GetWariorCell(X,Y)=nil);
  if CheckEnemys then  result:=result and (GetEnemyCell(X,Y)=nil);
end;

function TLevelScene.GetWariorCell(X, Y: Single): pointer;
 var i:integer;
begin

  if length(wariors) > 0 then
  begin
    for i := 0 to length(wariors) - 1 do
      if wariors[i] <> nil then  (*If still no created*)
      begin
        if (((round(wariors[i].Pos.X) div 32) + 1) <> round(X)) or
          (((round(wariors[i].Pos.Y) div 32) + 1) <> round(Y)) then
          result := nil
        else
        begin
          result := wariors[i];
          break;
        end;
      end;
  end
  else
    result := nil;


end;

function TLevelScene.GetEnemyCell(X, Y: Single): pointer;
var i:integer;
begin

  if length(enemys) > 0 then
  begin
    for i := 0 to length(enemys) - 1 do
      if enemys[i] <> nil then  (*If still no created*)
      begin
        if (enemys[i].X <> round(X)) or
           (enemys[i].Y <> round(Y)) then
          result := nil
        else
        begin
          result := enemys[i];
          break;
        end;
      end;
  end
  else
    result := nil;
end;

function TLevelScene.RayTrace(ray: zglPLine): boolean;
var physField:array of zglTRect;
  i,j:integer;
begin
  result:=false;
  for j:=1 to 32 do
   for i:=1 to 32 do
     if (levMap[i,j]<>0)and(levMap[i,j]<>3)and(levMap[i,j]<>2)and(levMap[i,j]<7) then
       begin
         setLength(physField,length(PhysField)+1);
         physField[length(PhysField)-1].H:=32;
         physField[length(PhysField)-1].W:=32;
         physField[length(PhysField)-1].X:=i*32-32;
         physField[length(PhysField)-1].Y:=j*32-32;
       end;
    for i:=0 to  length(physField)-1 do
      if col2d_LineVsRect(ray^,physField[i],nil) then
       begin
         result:=true;
         break;
       end;
    setLength(physField,0)
end;

end.
