unit GameUnits;

{$mode delphi}

interface

uses
  Classes, SysUtils, zglHeader, var_const,Units_Panel,zglGUI;

const
  UnitZoomer = 1;

type

  { TGameUnit }


  TGameUnit = class
  protected

    CPU_Controled: boolean;
    ShellType{Тип снаряда}: byte;

    Health, Speed, Damage, FireSpeed, ShellSpeed,
    ShellCurving: single;
    startingHealth:single;
    tex: zglPtexture;
    Direction: single;

    angle: single;

    selected: boolean;
    movePoints: array of zglTpoint2D;

    shell:TShell;
    shooting:boolean;
    Emitter:zglPEmitter2d;
    ShellEmitter:zglPEmitter2d;

    range:single;

    procedure reCalcMovePoints;
    procedure DelShell;
  public
    class_:byte;
    Waypoint:zglTPoint2d;
    Moving:boolean;
    moveIsNeeded:boolean;
    Pos: zglTPoint2d;
    Size: single;
    constructor Create(type_: byte; X, Y: single; CPU: boolean);
    procedure Update(delta: double);
    procedure Draw;
    procedure DrawSelected;
    procedure SetPos(X, Y: single);
    procedure addWayPoint(X, Y: single);
    procedure select;
    procedure deselect;
    function isAbleToMove(X, Y: single): boolean;
    procedure Shoot(X, Y: single);
    procedure Hurt(howMany:single);
    function isKilled:boolean;
    procedure Kill;
    procedure tryToHeal(objective:pointer);
  end;

implementation

uses LevelUnit,Enemy_Unit;

{ TGameUnit }

constructor TGameUnit.Create(type_: byte; X, Y: single; CPU: boolean);
begin
  Pengine2d_Set(@particles);
  moving:=false;
  CPU_Controled := CPU;
  class_ := type_;
  angle := 3 - random(6);
  case class_ of
    Unit_Soldier:
    begin
      tex := soldiertex;
      Health := 100;
      Speed := 125;
      Damage := 50;
      Size := 24 * UnitZoomer;
      ShellType := Shell_Normal;
      FireSpeed := 1;  {Shoot's in sec}
      ShellSpeed := 600; { px in sec}
      ShellCurving := 5; {Degreases}
      range:=250;

    end;
    Unit_RocketMan:
    begin
      tex := rocketmantex;
      Health := 100;
      Speed := 125;
      Damage := 75;
      Size := 24 * UnitZoomer;
      ShellType := Shell_Rocket;
      FireSpeed := 1;  {Shoot's in sec}
      ShellSpeed := 600; { px in sec}
      ShellCurving := 5; {Degreases}
      range:=350
    end;
    Unit_Tank:
    begin
      tex := tanktex;
      Health := 200;
      Speed := 125;
      Damage := 50;
      Size := 24 * UnitZoomer;
      ShellType := Shell_normal;
      FireSpeed := 1;  {Shoot's in sec}
      ShellSpeed := 600; { px in sec}
      ShellCurving := 7; {Degreases}
      range:=250;
    end;
    Unit_Medic:
    begin
      tex := medictex;
      Health := 85;
      Speed := 125;
      Damage := 0;
      Size := 24 * UnitZoomer;
      ShellType := Shell_normal;
      FireSpeed := 1;  {Shoot's in sec}
      ShellSpeed := 600; { px in sec}
      ShellCurving := 8; {Degreases}
      range:=0;
    end;
    Unit_Capitan:
    begin
      tex := Capitantex;
      Health := 115;
      Speed := 125;
      Damage := 60;
      Size := 24 * UnitZoomer;
      ShellType := Shell_Granade;
      FireSpeed := 1;  {Shoot's in sec}
      ShellSpeed := 600; { px in sec}
      ShellCurving := 10; {Degreases}
      range:=400;
    end;
    Unit_Sniper:
    begin
      tex := SniperTex;
      Health := 75;
      Speed := 125;
      Damage := 90;
      Size := 24 * UnitZoomer;
      ShellType := Shell_Normal;
      FireSpeed := 1;  {Shoot's in sec}
      ShellSpeed := 600; { px in sec}
      ShellCurving := 4; {Degreases}
      Shooting:=false;
      range:=750;
    end
  end;
  startingHealth:=Health;
  //addWayPoint(X,Y);
  SetPos(X, Y);
  ReCalcMovepoints;
  pengine2d_addEmitter(shellTracerEmitter,@ShellEmitter,-9999,-9999);
end;

procedure TGameUnit.Update(delta: double);
var
  waycircle: zglTCircle;
  objective:pointer;
  tempDamage:integer;
begin
  Pengine2d_Set(@particles);
  if moving then
       begin

        TLevelScene(GameScene).selectedUnit:=-9999;
        deselect;
        if Emitter<>nil then
         begin
          Emitter^.Params.Position.X:=Pos.X;
          Emitter^.Params.Position.Y:=Pos.Y;
         end;

         if (class_<>Unit_Medic)and(class_<>Unit_Sniper) then
         begin

         waycircle.cX := (wayPoint.X) * 32 - 16;
         waycircle.cY := (wayPoint.Y) * 32 - 16;
         WayCircle.Radius:=16;
         if not  col2d_pointinCircle(pos.X, pos.Y, waycircle) then
          begin
            pos.X += cos((direction+180)*deg2rad) * speed * (delta / 1000);
            pos.Y += sin((direction+180)*deg2rad) * speed * (delta / 1000);


            snd_Play(StepSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
          end
         else
         begin
         Emitter^.Params.Loop:=false;
         moving:=false;
         pos.X:= (wayPoint.X) * 32 - 16;
         pos.Y:= (wayPoint.Y) * 32 - 16;
         if TLevelScene(GameScene).movesLeft>0 then
         dec(TLevelScene(GameScene).movesLeft);
         end;
         ReCalcMovepoints;
         end else
         begin
          pengine2d_AddEmitter(teleportEmitter,nil,pos.X,pos.Y);
          moving:=false;
          pos.X:= (wayPoint.X) * 32 - 16;
          pos.Y:= (wayPoint.Y) * 32 - 16;
          snd_Play(TeleportSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
          pengine2d_AddEmitter(teleportEmitter,nil,pos.X,pos.Y);
         if TLevelScene(GameScene).movesLeft>0 then
         dec(TLevelScene(GameScene).movesLeft);
         end;



       end;
      if shooting then begin
       //TLevelScene(GameScene).selectedUnit:=-9999;
      // deselect;

          shell.pos.X += cos((shell.direction+180)*deg2rad) * shell.speed * (delta / 1000);
          shell.pos.Y += sin((shell.direction+180)*deg2rad) * shell.speed * (delta / 1000);
          if not TLevelScene(GameScene).CellIsFree(round(shell.pos.X) div 32 + 1,round(shell.pos.Y) div 32 + 1,false,false,true) then
          begin
               objective:=TLevelScene(GameScene).GetEnemyCell(round(shell.pos.X) div 32 + 1,round(shell.pos.Y) div 32 + 1);
               if objective <> nil then
               begin
                 if m_Distance(pos.X,pos.Y,Shell.pos.X,Shell.pos.Y)>range then
                 TempDamage:=round(damage*(range/m_Distance(pos.X,pos.Y,Shell.pos.X,Shell.pos.Y))) else
                 TempDamage:=round(Damage);
               TEnemy(objective).Hurt(TempDamage);
               Log1.Clear;
               Log1.AddItem(zglTListItem.Create(Log, 'Damage - '+inttostr(TempDamage), 16));
               end;
               delShell;
               snd_Play(MissSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
               if TLevelScene(GameScene).movesLeft>0 then
               dec(TLevelScene(GameScene).movesLeft);
          end;

          ShellEmitter.Params.Position:=Shell.pos;
          ShellEmitter.AsPoint.Direction:=Shell.direction-90;
    end

      else

    begin
       ShellEmitter.Params.Position.X:=-999;
       ShellEmitter.Params.Position.Y:=-999;
    end;
     // if ShellEmitter<>nil then
     //  begin
      // end;
  reCalcMovePoints;

end;

procedure TGameUnit.Draw;
begin
   if shooting then
   ssprite2d_draw(shellTex_0,shell.pos.X-4,shell.pos.Y-4,8,8,0);

  ssprite2d_draw(tex, pos.X - Size / 2, Pos.Y - Size / 2, size, size, angle);
  if selected then
    drawSelected;         //UP ^
end;

procedure TGameUnit.DrawSelected;
var
  i: integer;
begin
  pr2d_circle(Pos.X, Pos.Y, Size * 2, $0000AA, 50, 32, Pr2d_fill);
  if class_=UNIT_MEDIC then
  pr2d_circle(Pos.X, Pos.Y, Size * 3, $AA0000, 50, 32, Pr2d_fill);
  if length(movePoints) > 0 then
    for i := 0 to length(movePoints) - 1 do
      ssprite2d_draw(tracertex, (((Round(Pos.X) div 32) + 1) + movePoints[i].X) * 32 - 32,
        (((round(Pos.Y) div 32) + 1) + movePoints[i].Y) * 32 - 32, 32, 32, 0, 55);

  pr2d_rect((Round(Pos.X) div 32)*32,(Round(Pos.Y) div 32)*32,32,10,$000000,200,PR2D_FILL);
  pr2d_rect((Round(Pos.X) div 32)*32+2,(Round(Pos.Y) div 32)*32+2,(32-4)*Health/StartingHealth,6,$00EE00,100,PR2D_FILL);
end;

procedure TGameUnit.SetPos(X, Y: single);
begin
  pos.X := X * 32 - 16;
  pos.Y := Y * 32 - 16;
end;

procedure TGameUnit.addWayPoint(X, Y: single);
var i:integer;
  moveisallowed:boolean;
begin
   moveisallowed:=true;
 Pengine2d_Set(@particles);

 for i:= 0 to length(TLevelScene(GameScene).Wariors)-1 do
   if  TLevelScene(GameScene).Wariors[i].Moving then
      begin
        moveisallowed:=false;
        break;
      end;

 if (not shooting)and(TLevelScene(GameScene).movesLeft>0)and(moveIsAllowed) then
  if not moving then
      if (X>0)and(X<33)and(Y>0)and(Y<33) then
       begin

         moving:=true;
       if (class_<>Unit_Medic)and(class_<>Unit_Sniper) then
        if Emitter=nil then
           pengine2d_AddEmitter(tracerEmitter,@Emitter,Pos.X,Pos.Y);

          waypoint.X := X;
          waypoint.Y := Y;

          direction :=m_angle(pos.X,pos.Y, wayPoint.X *32 - 16,wayPoint.Y *32 - 16);
      // if direction=0 then direction:=180.00001;
      end;
end;

procedure TGameUnit.select;
begin
  selected := True;
end;

procedure TGameUnit.deselect;
begin
  selected := False;
end;

function TGameUnit.isAbleToMove(X, Y: single): boolean;
var
  i: integer;
begin
  Result := False;
  for i := 0 to length(movePoints) - 1 do
    if (round(round(Pos.X) div 32 + 1 + movePoints[i].X) = X) and
      (round(round(Pos.Y) div 32 + 1 + movePoints[i].Y) = Y) then
    begin
      Result := True;
      break;
    end;
end;

procedure TGameUnit.Shoot(X, Y: single);
begin
// PenGine2d_ClearAll;
 if  (not shooting)and(TLevelScene(GameScene).movesLeft>0)and(not moving) then
 begin
  shell.pos := pos;
  shell.direction :=
    m_angle(shell.pos.X, shell.pos.Y, X, Y)-
    ShellCurving/2+Random(round(ShellCurving));
  shell.type_ := shellType;
  shell.speed := shellSpeed;

 // if ShellEmitter<>nil then
 //  pengine2d_DelEmitter(ShellEmitter.ID);

  //pengine2d_addEmitter(shellTracerEmitter,@ShellEmitter,Shell.pos.X,Shell.pos.Y);

  ShellEmitter.Params.Position.X:=Shell.pos.X;
  ShellEmitter.Params.Position.Y:=Shell.pos.Y;

  shooting:=true;
  snd_Play(BlasterSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));

 end;
end;

procedure TGameUnit.Hurt(howMany: single);
begin
    Health -=howMany;
end;

function TGameUnit.isKilled: boolean;
begin
    if health<=0 then result:=true else result:=false;
end;

procedure TGameUnit.Kill;
begin
 pengine2d_set(@particles);
 pengine2d_DelEmitter(ShellEmitter.ID);
 pengine2d_AddEmitter(BoomEmitter,nil,pos.X,Pos.Y);
 snd_Play(BoomSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
end;

procedure TGameUnit.tryToHeal(objective: pointer);
var tempCircle:zglTCircle;
begin
 Pengine2d_Set(@particles);
  tempCircle.Cx:=Pos.X;
  tempCircle.cY:=Pos.Y;
  tempCircle.Radius:=Size*3;
 if (objective<>nil)and(objective<>self) then
 begin
  if col2d_PointInCircle(TGameUnit(objective).Pos.X,TGameUnit(objective).Pos.Y,tempCircle)and
    (TGameUnit(objective).Health<TGameUnit(objective).startingHealth) then
   begin
    TGameUnit(objective).Health+=TGameUnit(objective).startingHealth/4;
  if TGameUnit(objective).Health>TGameUnit(objective).startingHealth then
    TGameUnit(objective).Health:=TGameUnit(objective).startingHealth;
  pengine2d_addEmitter(HealEmitter,nil,TGameUnit(objective).Pos.X,TGameUnit(objective).Pos.Y);
  if TLevelScene(GameScene).movesLeft>0 then
  dec(TLevelScene(GameScene).movesLeft);
  snd_Play(healSound,false,CalcX2d(Game.GetScreenW()/2),CalcY2d(Game.GetScreenH()/2));
   end;
 end;
end;

procedure TGameUnit.reCalcMovePoints;
var
  i, j: integer;
begin
  setlength(movePoints, 0);
  case class_ of
    UNIT_SOLDIER:
    begin
      setlength(movePoints, 8);
      if TLevelScene(GameScene).CellIsFree(-1 + (round(pos.X) div 32) + 1, -1 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[0].X := -1;
        movePoints[0].Y := -1;
      end;
      if TLevelScene(GameScene).CellIsFree(0 + (round(pos.X) div 32) + 1, -1 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[1].X := 0;
        movePoints[1].Y := -1;
      end;
      if TLevelScene(GameScene).CellIsFree(1 + (round(pos.X) div 32) + 1, -1 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[2].X := 1;
        movePoints[2].Y := -1;
      end;
      if TLevelScene(GameScene).CellIsFree(1 + (round(pos.X) div 32) + 1, 0 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[3].X := 1;
        movePoints[3].Y := 0;
      end;
      if TLevelScene(GameScene).CellIsFree(1 + (round(pos.X) div 32) + 1, 1 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[4].X := 1;
        movePoints[4].Y := 1;
      end;
      if TLevelScene(GameScene).CellIsFree(0 + (round(pos.X) div 32) + 1, 1 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[5].X := 0;
        movePoints[5].Y := 1;
      end;
      if TLevelScene(GameScene).CellIsFree(-1 + (round(pos.X) div 32) + 1, 1 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[6].X := -1;
        movePoints[6].Y := 1;
      end;
      if TLevelScene(GameScene).CellIsFree(-1 + (round(pos.X) div 32) + 1, 0 +
        (round(pos.Y) div 32) + 1,true,true,true) then
      begin
        movePoints[7].X := -1;
        movePoints[7].Y := 0;
      end;

    end;
    UNIT_ROCKETMAN:
    begin
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(i +
          (round(pos.X) div 32) + 1, i + (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := i;
          movePoints[length(movepoints) - 1].Y := i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(-i +
          (round(pos.X) div 32) + 1, -i + (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := -i;
          movePoints[length(movepoints) - 1].Y := -i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(i +
          (round(pos.X) div 32) + 1, -i + (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := i;
          movePoints[length(movepoints) - 1].Y := -i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(-i +
          (round(pos.X) div 32) + 1, i + (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := -i;
          movePoints[length(movepoints) - 1].Y := i;
        end
        else
          break;

      end;
    end;
    UNIT_TANK:
    begin
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(i + (round(pos.X) div 32) + 1, 0 +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := i;
          movePoints[length(movepoints) - 1].Y := 0;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(-i + (round(pos.X) div 32) + 1, 0 +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := -i;
          movePoints[length(movepoints) - 1].Y := 0;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(0 + (round(pos.X) div 32) + 1, -i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := 0;
          movePoints[length(movepoints) - 1].Y := -i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(0 + (round(pos.X) div 32) + 1, i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := 0;
          movePoints[length(movepoints) - 1].Y := i;
        end
        else
          break;

      end;
    end;
    UNIT_MEDIC:
    begin
      for i := -3 to 3 do
        for j := -3 to 3 do
          if (i <> 0) or (j <> 0) then
            if TLevelScene(GameScene).CellIsFree(j + (round(pos.X) div 32) + 1, i +
              (round(pos.Y) div 32) + 1,true,true,true) then
            begin
              setLength(movepoints, length(movepoints) + 1);
              movePoints[length(movepoints) - 1].X := j;
              movePoints[length(movepoints) - 1].Y := i;
            end;
    end;
    UNIT_Capitan:
    begin
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(i + (round(pos.X) div 32) + 1, i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := i;
          movePoints[length(movepoints) - 1].Y := i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(-i +
          (round(pos.X) div 32) + 1, -i + (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := -i;
          movePoints[length(movepoints) - 1].Y := -i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(i + (round(pos.X) div 32) + 1, -i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := i;
          movePoints[length(movepoints) - 1].Y := -i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(-i + (round(pos.X) div 32) + 1, i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := -i;
          movePoints[length(movepoints) - 1].Y := i;
        end
        else
          break;
      end;

      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(i + (round(pos.X) div 32) + 1, 0 +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := i;
          movePoints[length(movepoints) - 1].Y := 0;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(-i + (round(pos.X) div 32) + 1, 0 +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := -i;
          movePoints[length(movepoints) - 1].Y := 0;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(0 + (round(pos.X) div 32) + 1, -i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := 0;
          movePoints[length(movepoints) - 1].Y := -i;
        end
        else
          break;

      end;
      for i := 1 to 31 do
      begin

        if TLevelScene(GameScene).CellIsFree(0 + (round(pos.X) div 32) + 1, i +
          (round(pos.Y) div 32) + 1,true,true,true) then
        begin
          setLength(movepoints, length(movepoints) + 1);
          movePoints[length(movepoints) - 1].X := 0;
          movePoints[length(movepoints) - 1].Y := i;
        end
        else
          break;

      end;
    end;
    UNIT_Sniper:
    begin
      for i := -2 to 2 do
        for j := -2 to 2 do
          if (i <> 0) or (j <> 0) then
            if TLevelScene(GameScene).CellIsFree(j + (round(pos.X) div 32) + 1, i +
              (round(pos.Y) div 32) + 1,true,true,true) then
            begin
              setLength(movepoints, length(movepoints) + 1);
              movePoints[length(movepoints) - 1].X := j;
              movePoints[length(movepoints) - 1].Y := i;
            end;
    end;
  end;

end;

procedure TGameUnit.DelShell;
begin
  Shooting:=false;
 // if ShellEmitter<>nil then
 // pengine2d_delEmitter(ShellEmitter.ID)
   //ShellEmitter.Params.Loop:=false;

end;

end.
