unit _Units;

interface

uses
  _Geometry,
  _Render;

const
 default_size = 30;

 to_platform = 5;//go back turret to clear platform
 //Actions
 a_none = 0;
 a_move = 1;
 a_ready = 2;
 a_die = 9;//boom :)
 a_free = 10;//can free unit
 death_anim=320;
 //Unit_type
 ut_Ground = 0;
 ut_Armored = 1;
 ut_Air = 2;
 ut_Structure = 3;
 ut_UnderGround = 4;

type
 //weapons
  TGun = class
    reload_time,
    reload_timer:byte;
    distanse:integer;
    damage:byte;
    //for bullet
    fire:boolean;
    Anim,Fly_time:byte;
    Angle:single;
    Coord,Vector:TVector;
  end;

  TGausgun = class 
    reload_time,
    reload_timer:byte;
    distanse:integer;
    damage:byte;
    //for bullet
    fire:boolean;
    Anim,Fly_time:byte;
    Angle:single;
    Coord,Vector:TVector;
  end;

 //units
  TBaseUnit = class
   private
   public                                 
    Anim:integer;               //for death animation
    Action:byte;       //for ai
    Life:integer;
    Coord:TVector;
    Next_Way_Point,   //Need to walk in this point
    //todo
    Target_Way_Point:TVector;//final point
    View_Radius:single;
    Size:single;
    Side:byte;         
    has_target:boolean;
    Color:Cardinal;
    Target:word;     //index of target enemy
    Target_Coord:TVector;
    State:boolean;
    tip:byte;
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);Virtual;abstract;
    procedure Draw(var Camera:TVector; var Scale:Single);Virtual;abstract;
    procedure Move;Virtual;abstract;
    function Fire:boolean;Virtual;abstract;
    function Enemy_Hit(tip:byte):integer;Virtual;abstract;
    function Can_Hit(tip:byte):boolean;Virtual;abstract;
    procedure Change_State;
  end;

  TTank = class (TBaseUnit)
    Angle,
    Need_Angle,
    Cannon_Angle,
    Need_Cannon_Angle:Single;
    Speed,Max_Speed:single;     //0 for static constructions
    Angle_Speed:single;         //1 minimum
    Gun:TGun;             //Gun type(bullet type)
    Convert_anim:word;
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    procedure Move;override;
    function Fire:boolean;override;
    function Enemy_Hit(tip:byte):integer;override;
    function Can_Hit(tip:byte):boolean;override;
  end;

  TTransport = class(TBaseUnit)
    Angle,
    Need_Angle,
    Cannon_Angle,
    Need_Cannon_Angle:Single;
    Speed,Max_Speed:single;     //0 for static constructions
    Angle_Speed:single;         //1 minimum
    Convert_anim:word;
    Gaus:array [0..2] of TGausgun;             //Gun type(bullet type)
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    procedure Move;override;
    function Fire:boolean;override;
    function Enemy_Hit(tip:byte):integer;override;
    function Can_Hit(tip:byte):boolean;override;
  end;

  THeavyTank = class (TTank)
    procedure Draw(var Camera:TVector; var Scale:Single);override;
  end; 

  THeli = class (TBaseUnit)
    Angle,
    Need_Angle,
    Cannon_Angle,
    Need_Cannon_Angle:Single;
    Rotor_ang:single;
    Height:byte;
    Speed,Max_Speed:single;     //0 for static constructions
    Angle_Speed:single;         //1 minimum
    Gaus:array [0..2] of TGausgun;             //Gun type(bullet type)
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    procedure Move;override;
    function Fire:boolean;override;
    function Enemy_Hit(tip:byte):integer;override;
    function Can_Hit(tip:byte):boolean;override;
  end;

  THeliTank = class (THeli)       
    Convert_anim:word;
    procedure Move;override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    function Enemy_Hit(tip:byte):integer;override;
  end;

  TBasePlatform = class (TBaseUnit)
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    procedure Move;override;
    function Fire:boolean;override;
    function Enemy_Hit(tip:byte):integer;override;
    function Can_Hit(tip:byte):boolean;override;
  end;

  TBase = class (TBasePlatform)
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;    
    procedure Move;override;
  end;

  TTurret = class (TBaseUnit)
    Cannon_Angle,
    Need_Cannon_Angle:Single;
    Angle_Speed:single;         //1 minimum
    Gun:TGun;             //Gun type(bullet type)   
    Max_Life:integer;
    Convert_anim:word;
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    procedure Move;override;
    function Fire:boolean;override;
    function Enemy_Hit(tip:byte):integer;override;     
    function Can_Hit(tip:byte):boolean;override;
  end;

  TGaus = class (TBaseUnit)
    Cannon_Angle,
    Need_Cannon_Angle:Single;              
    Max_Life:integer;
    Angle_Speed:single;         //1 minimum
    Gaus:array [0..3] of TGausgun;             //Gun type(bullet type)
    Convert_anim:word;
    constructor Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);override;
    procedure Draw(var Camera:TVector; var Scale:Single);override;
    procedure Move;override;
    function Fire:boolean;override;
    function Enemy_Hit(tip:byte):integer;override;     
    function Can_Hit(tip:byte):boolean;override;
  end;

implementation
//=== Base Unit===
{$REGION 'State'}
procedure TBaseUnit.Change_State;
begin
 State:=not State;
end;
{$ENDREGION}
// ====   TANK   ====
{$REGION 'Create Tank'}
constructor TTank.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
begin
 tip:=ut_ground;     
 Life:=100;
 anim:=0;
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=plus(Crd,Vp);
 Angle:=Ang;
 Need_Angle:=Ang;
 Cannon_Angle:=Random(360); // :)
 Need_Cannon_Angle:=Angle;
 Angle_Speed:=4;
 Speed:=2;
 Action:=0;
 View_Radius:=1000;
 Max_Speed:=3;
 Gun:=TGun.Create;
 Gun.reload_timer:=80;
 Gun.reload_time:=Random(Gun.reload_timer);
 Gun.distanse:=1000;
 Gun.damage:=10;
 State:=true; 
 Size:=default_size;
 Color:=$ffffffff;
 Convert_anim:=0;
 State:=false;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure TTank.Draw(var Camera:TVector; var Scale:Single);
var t:byte;
begin
 if Action = a_die then
  if (Anim>200) then
   Color:=Color-$01000000;
 if Convert_anim<20 then t:=0 else
  if Convert_anim<254 then t:=1 else t:=2;

  Draw_Tank((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,Angle+90-Convert_anim,Cannon_Angle+90,Scale,Color,0+t,12);
 if Gun.fire then
  Draw_Bullet(multi(minus(plus(Gun.Coord,multi(Gun.Vector,Gun.Anim)),Camera),Scale),Gun.Angle+90,Scale);
end;
{$ENDREGION}

{$REGION 'Move'}
procedure TTank.Move;
begin
 if Action>=a_die then anim:=anim+1;
 if (anim>0) then
  if (anim<100) or (random(3)=0) and (anim<300) then
   Emiter.add_particle(Coord,Vector((Random(10)-5)/6,(Random(10)-5)/6),random(2),30);
 if (anim>=death_anim) then Action:=a_free;
 
 if Action<a_die then
 if state then
  begin
   if (Convert_anim<254) then Convert_anim:=Convert_anim+2;
  end else
   if (Convert_anim>0) then Convert_anim:=Convert_anim-2;
  if (Convert_anim>240) then
   tip:=ut_Armored else
   tip:=ut_Ground;


 if Action>=a_die then exit;
 if (Convert_anim<10) then
  Angle:=Head_Rotate(Angle,Need_Angle,Angle_Speed);
 Cannon_Angle:=Head_Rotate(Cannon_Angle,Need_Cannon_Angle,Angle_Speed);   
 //find need move angle
// if (ai_timer mod 10 = i mod 10) then
  if ((abs(Coord[0]-Next_Way_Point[0])>Max_Speed*2) or
      (abs(Coord[1]-Next_Way_Point[1])>Max_Speed*2)) then
  begin
   Need_Angle:=Get_A(Coord,Next_Way_Point);
   Action:=a_move;
   if (Speed<Max_Speed) then
    Speed:=Speed*0.9+Max_Speed*0.1;
  end else
   if Action<>A_die then
    Action:=A_none;
 //slow stopping
 if (Action=a_move) then
  if (abs(Coord[0]-Next_Way_Point[0])<=Max_Speed*5) then
  if (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed*5) then
    Speed:=Speed*0.75;

  if ((abs(Coord[0]-Next_Way_Point[0])<=Max_Speed) and
      (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed)) then
     begin
      Coord:=Next_Way_Point;
      Action:=a_none;
      Speed:=0;
     end;
 //Move unit
 if (Action=a_move) and (Convert_anim<10) then
  Coord:=plus(Coord,Get_Vect(Angle,Speed));
end;
{$ENDREGION}

{$REGION 'Fire'}
function TTank.Fire:boolean;
var s:single;
begin
 Result:=false;
 Need_Cannon_Angle:=Get_A(Coord,Target_Coord);
 //Gun reload
 if (Gun.reload_time>0) then dec(Gun.reload_time);
 //Fire
 if (Gun.reload_time=0) then
 if has_target then
//  if (Get_S(Coord,Target_Coord)<Gun.distanse) then
  if (Get_R(Need_Cannon_Angle-Cannon_Angle)<5) then
  begin
    Gun.reload_time:=Gun.reload_timer;
    Gun.Fire:=true;
    Gun.Anim:=0;
    Gun.Angle:=Get_A(Coord,Target_Coord);
    Gun.Coord:=Coord;
    s:=Get_S(Coord,Target_Coord);
    if s>50 then
     Gun.Fly_time:=trunc(s/50) else
    Gun.Fly_time:=trunc(s/10);
    Gun.Vector:=multi(minus(plus(Target_Coord,Vector(Random(20)-10,Random(20)-10)),Coord),1/Gun.Fly_time);
  end;
 //bullet animation and
 if Gun.Fire then inc(Gun.Anim);
 if (Gun.Anim>=Gun.Fly_time+1) then
  begin                  
    Gun.Anim:=0;
    Gun.Fire:=false;
    Result:=true;
  end;
 Fire:=result;
end;

{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function TTank.Can_Hit(tip:byte):boolean;
begin
 Can_Hit:=(tip<>ut_Air) and (tip<>ut_UnderGround);
end;

function TTank.Enemy_Hit;
begin
 if tip=ut_Armored then
  Enemy_Hit:=(Gun.Damage*(Random(4)+1) div 2) else
 Enemy_Hit:=Gun.Damage*(Random(4)+1);
end;
{$ENDREGION}

// ====   Transport   ====
{$REGION 'Create Transport'}
constructor TTransport.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
var i:byte;
begin
 tip:=ut_ground;     
 Life:=80;
 anim:=0;
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=plus(Crd,Vp);
 Angle:=Ang;
 Need_Angle:=Ang;
 Cannon_Angle:=Random(360); // :)
 Need_Cannon_Angle:=Angle;
 Angle_Speed:=4;
 Speed:=2;
 Action:=0;
 View_Radius:=1000;
 Max_Speed:=3;   
 for i:=0 to 2 do
   begin
     Gaus[i]:=TGausgun.Create;
     Gaus[i].reload_timer:=20;
     Gaus[i].reload_time:=Random(Gaus[i].reload_timer);
     Gaus[i].distanse:=1000;
     Gaus[i].damage:=2;
   end;

 State:=true; 
 Size:=default_size;
 Color:=$ffffffff;
 Convert_anim:=0;
 State:=false;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure TTransport.Draw(var Camera:TVector; var Scale:Single);
var i:byte;
begin
 if Action = a_die then
  if (Anim>200) then
   Color:=Color-$01000000;

  Draw_Tank((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,Angle+90-Convert_anim,Cannon_Angle+90,Scale,Color,5,14);
 for i:=0 to 2 do   
  if Gaus[i].fire then
   Draw_Gaus(multi(minus(plus(Gaus[i].Coord,multi(Gaus[i].Vector,Gaus[i].Anim)),Camera),Scale),Gaus[i].Angle+90,Scale);
end;
{$ENDREGION}

{$REGION 'Move'}
procedure TTransport.Move;
begin
 if Action>=a_die then anim:=anim+1;
 if (anim>0) then
  if (anim<100) or (random(3)=0) and (anim<300) then
   Emiter.add_particle(Coord,Vector((Random(10)-5)/6,(Random(10)-5)/6),random(2),30);
 if (anim>=death_anim) then Action:=a_free;
 
 if Action<a_die then
 if state then
  begin
   if (Convert_anim<254) then Convert_anim:=Convert_anim+2;
  end else
   if (Convert_anim>0) then Convert_anim:=Convert_anim-2;
  if (Convert_anim>240) then
   tip:=ut_Armored else
   tip:=ut_Ground;


 if Action>=a_die then exit;
 if (Convert_anim<10) then
  Angle:=Head_Rotate(Angle,Need_Angle,Angle_Speed);
 Cannon_Angle:=Head_Rotate(Cannon_Angle,Need_Cannon_Angle,Angle_Speed);   
 //find need move angle
// if (ai_timer mod 10 = i mod 10) then
  if ((abs(Coord[0]-Next_Way_Point[0])>Max_Speed*2) or
      (abs(Coord[1]-Next_Way_Point[1])>Max_Speed*2)) then
  begin
   Need_Angle:=Get_A(Coord,Next_Way_Point);
   Action:=a_move;
   if (Speed<Max_Speed) then
    Speed:=Speed*0.9+Max_Speed*0.1;
  end else
   if Action<>A_die then
    Action:=A_none;
 //slow stopping
 if (Action=a_move) then
  if (abs(Coord[0]-Next_Way_Point[0])<=Max_Speed*5) then
  if (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed*5) then
    Speed:=Speed*0.75;

  if ((abs(Coord[0]-Next_Way_Point[0])<=Max_Speed) and
      (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed)) then
     begin
      Coord:=Next_Way_Point;
      Action:=a_none;
      Speed:=0;
     end;
 //Move unit
 if (Action=a_move) and (Convert_anim<10) then
  Coord:=plus(Coord,Get_Vect(Angle,Speed));
end;
{$ENDREGION}

{$REGION 'Fire'}
function TTransport.Fire:boolean;
var
 s:single;
 i:byte;
begin
 Result:=false;
 Need_Cannon_Angle:=Get_A(Coord,Target_Coord);
 if (Gaus[0].reload_time>0) and (Gaus[0].reload_time=Gaus[1].reload_time) then
  Gaus[0].reload_time:=Gaus[0].reload_time+5;
 if (Gaus[1].reload_time>0) and (Gaus[1].reload_time=Gaus[2].reload_time) then
  Gaus[1].reload_time:=Gaus[1].reload_time+5;

 for i:=0 to 2 do
 begin
 //Rockets reload
 if (Gaus[i].reload_time>0) then dec(Gaus[i].reload_time);
 //Fire
 if (Gaus[i].reload_time=0) then
 if has_target then
 // if (Get_S(Coord,Target_Coord)<Gaus[i].distanse) then
//  if (Get_R(Need_Cannon_Angle-Cannon_Angle)<5) then
  begin
    Gaus[i].reload_time:=Gaus[i].reload_timer;
    Gaus[i].Fire:=true;
    Gaus[i].Anim:=0;
    Gaus[i].Angle:=Get_A(Coord,Target_Coord);
    Gaus[i].Coord:=Coord;
    s:=Get_S(Coord,Target_Coord);
    if s>50 then
     Gaus[i].Fly_time:=trunc(s/50) else
    Gaus[i].Fly_time:=trunc(s/10);
    Gaus[i].Vector:=multi(minus(plus(Target_Coord,Vector(Random(20)-10,Random(20)-10)),Coord),1/Gaus[i].Fly_time);
  end;
 //bullet animation and
 if Gaus[i].Fire then inc(Gaus[i].Anim);
 if (Gaus[i].Anim>=Gaus[i].Fly_time+1) then
  begin                  
    Gaus[i].Anim:=0;
    Gaus[i].Fire:=false;
    Result:=true;
  end;
 end;
 Fire:=result;
end;

{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function TTransport.Can_Hit(tip:byte):boolean;
begin
 Can_Hit:=(tip=ut_Air);
end;

function TTransport.Enemy_Hit;
begin
 Enemy_Hit:=Gaus[0].Damage;
end;
{$ENDREGION}

// ====   Heavy Tank   ====
{$REGION 'draw'}
procedure THeavyTank.Draw(var Camera:TVector; var Scale:Single);
begin              
 if Action = a_die then
  if (Anim>200) then
   Color:=Color-$01000000;
 Draw_Tank((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,Angle+90,Cannon_Angle+90,Scale*1.2,Color,4,13);
 if Gun.fire then
  Draw_Double_Bullet(multi(minus(plus(Gun.Coord,multi(Gun.Vector,Gun.Anim)),Camera),Scale),Gun.Angle+90,Scale);
end;
{$ENDREGION}

// ====   Helicopter   ====
{$REGION 'Create heli'}
constructor THeli.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
var i:byte;
begin
 tip:=ut_air;
 Life:=100;
 anim:=0;
 Height:=250;
 Rotor_ang:=Random(360);
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=plus(Crd,Vp);
 Angle:=Ang;
 Need_Angle:=Ang;
 Cannon_Angle:=Random(360); // :)
 Need_Cannon_Angle:=Angle;
 Angle_Speed:=4;
 Speed:=2;
 Action:=0;
 View_Radius:=1000;
 Max_Speed:=4;
 for i:=0 to 2 do
   begin
     Gaus[i]:=TGausgun.Create;
     Gaus[i].reload_timer:=20;
     Gaus[i].reload_time:=Random(Gaus[i].reload_timer);
     Gaus[i].distanse:=1000;
     Gaus[i].damage:=2;
   end;
                       
 Color:=$ffffffff;
 Size:=default_size;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure THeli.Draw(var Camera:TVector; var Scale:Single);
var i:byte;
begin
 Draw_Heli((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,height/5,Angle+90,Scale,Rotor_ang,Color);
 for i:=0 to 2 do   
  if Gaus[i].fire then
   Draw_Gaus(multi(minus(plus(Gaus[i].Coord,multi(Gaus[i].Vector,Gaus[i].Anim)),Camera),Scale),Gaus[i].Angle+90,Scale);
end;
{$ENDREGION}

{$REGION 'Move'}
procedure THeli.Move;
var i:byte;
begin
 Rotor_ang:=Get_R(Rotor_ang-12);
 if Action>=a_die then inc(anim);
 if (anim>0) and (random(2)=0) then
  if (anim<100) or (random(5)=0) and (anim<300) then
   Emiter.add_particle(Coord,Vector((Random(10)-5)/6,(Random(10)-5)/6),random(2),30);    
 if (anim>=death_anim) then Action:=a_free;
 if anim>70 then
  dec(height);

 if Action>=a_die then
  begin
   if (anim=death_anim-2) then   
     for i:=0 to 30 do         
      Emiter.add_particle(
                  plus(Coord,Vector((Random(30)-15),(Random(30)-15)/2)),
                  Vector((Random(10)-5)/2,(Random(10)-5)/2),
                  random(2),
                  30);

   Coord:=plus(Coord,Get_Vect(Cannon_Angle,2));
   Cannon_Angle:=Get_R(Cannon_Angle+1);
   Angle:=Get_R(Angle+2);
   exit;
  end;
 Angle:=Head_Rotate(Angle,Need_Angle,Angle_Speed);
 Cannon_Angle:=Head_Rotate(Cannon_Angle,Need_Cannon_Angle,Angle_Speed);   
 //find need move angle
// if (ai_timer mod 10 = i mod 10) then
  if ((abs(Coord[0]-Next_Way_Point[0])>Max_Speed*2) or
      (abs(Coord[1]-Next_Way_Point[1])>Max_Speed*2)) then
  begin
   Need_Angle:=Get_A(Coord,Next_Way_Point);
   Action:=a_move;
   if (Speed<Max_Speed) then
    Speed:=Speed*0.9+Max_Speed*0.1;
  end else
   if Action<>A_die then
    Action:=A_none;
 //slow stopping
 if (Action=a_move) then
  if (abs(Coord[0]-Next_Way_Point[0])<=Max_Speed*5) then
  if (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed*5) then
    Speed:=Speed*0.75;

  if ((abs(Coord[0]-Next_Way_Point[0])<=Max_Speed) and
      (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed)) then
     begin
      Coord:=Next_Way_Point;
      Action:=a_none;
      Speed:=0;
     end;
 //Move unit
 if (Action=a_move) then
  Coord:=plus(Coord,Get_Vect(Angle,Speed));
end;
{$ENDREGION}

{$REGION 'Fire'}
function THeli.Fire:boolean;
var
 s:single;
 i:byte;
begin
 Result:=false;
 Need_Cannon_Angle:=Get_A(Coord,Target_Coord);
 if (Gaus[0].reload_time>0) and (Gaus[0].reload_time=Gaus[1].reload_time) then
  Gaus[0].reload_time:=Gaus[0].reload_time+5;
 if (Gaus[1].reload_time>0) and (Gaus[1].reload_time=Gaus[2].reload_time) then
  Gaus[1].reload_time:=Gaus[1].reload_time+5;

 for i:=0 to 2 do
 begin
 //Rockets reload
 if (Gaus[i].reload_time>0) then dec(Gaus[i].reload_time);
 //Fire
 if (Gaus[i].reload_time=0) then
 if has_target then
 // if (Get_S(Coord,Target_Coord)<Gaus[i].distanse) then
//  if (Get_R(Need_Cannon_Angle-Cannon_Angle)<5) then
  begin
    Gaus[i].reload_time:=Gaus[i].reload_timer;
    Gaus[i].Fire:=true;
    Gaus[i].Anim:=0;
    Gaus[i].Angle:=Get_A(Coord,Target_Coord);
    Gaus[i].Coord:=Coord;
    s:=Get_S(Coord,Target_Coord);
    if s>50 then
     Gaus[i].Fly_time:=trunc(s/50) else
    Gaus[i].Fly_time:=trunc(s/10);
    Gaus[i].Vector:=multi(minus(plus(Target_Coord,Vector(Random(20)-10,Random(20)-10)),Coord),1/Gaus[i].Fly_time);
  end;
 //bullet animation and
 if Gaus[i].Fire then inc(Gaus[i].Anim);
 if (Gaus[i].Anim>=Gaus[i].Fly_time+1) then
  begin                  
    Gaus[i].Anim:=0;
    Gaus[i].Fire:=false;
    Result:=true;
  end;
 end;
 Fire:=result;
end;

{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function THeli.Can_Hit(tip:byte):boolean;
begin
 Can_Hit:=tip<>ut_UnderGround;
end;

function THeli.Enemy_Hit;
begin                
 if tip=ut_Armored then
  Enemy_Hit:=(Gaus[0].Damage div 2) else
 Enemy_Hit:=Gaus[0].Damage;
end;
{$ENDREGION}

//=====  Helicopter-tank  =====
{$REGION 'Helitank draw'}
procedure THeliTank.Move;
var i:byte;
begin
 Rotor_ang:=Get_R(Rotor_ang-12);
 if Action>=a_die then inc(anim);
 if (anim>0) and (random(2)=0) then
  if (anim<100) or (random(5)=0) and (anim<300) then
   Emiter.add_particle(Coord,Vector((Random(10)-5)/6,(Random(10)-5)/6),random(2),30);    
 if (anim>=death_anim) then Action:=a_free;
 if anim>70 then
  dec(height);

 if Action<a_die then
 if state then
  begin
   if (Convert_anim<254) then Convert_anim:=Convert_anim+2;
  end else
   if (Convert_anim>0) then Convert_anim:=Convert_anim-2;
  if (Convert_anim>250) then
   tip:=ut_Ground else
   tip:=ut_Air;
 Height:=255-Convert_anim;


 if Action>=a_die then
  begin
   if (anim=death_anim-2) then   
     for i:=0 to 30 do         
      Emiter.add_particle(
                  plus(Coord,Vector((Random(30)-15),(Random(30)-15)/2)),
                  Vector((Random(10)-5)/2,(Random(10)-5)/2),
                  random(2),
                  30);

   Coord:=plus(Coord,Get_Vect(Cannon_Angle,2));
   Cannon_Angle:=Get_R(Cannon_Angle+1);
   Angle:=Get_R(Angle+2);
   exit;
  end;
 Angle:=Head_Rotate(Angle,Need_Angle,Angle_Speed);
 Cannon_Angle:=Head_Rotate(Cannon_Angle,Need_Cannon_Angle,Angle_Speed);   
 //find need move angle
// if (ai_timer mod 10 = i mod 10) then
  if ((abs(Coord[0]-Next_Way_Point[0])>Max_Speed*2) or
      (abs(Coord[1]-Next_Way_Point[1])>Max_Speed*2)) then
  begin
   Need_Angle:=Get_A(Coord,Next_Way_Point);
   Action:=a_move;
   if (Speed<Max_Speed) then
    Speed:=Speed*0.9+Max_Speed*0.1;
  end else
   if Action<>A_die then
    Action:=A_none;
 //slow stopping
 if (Action=a_move) then
  if (abs(Coord[0]-Next_Way_Point[0])<=Max_Speed*5) then
  if (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed*5) then
    Speed:=Speed*0.75;

  if ((abs(Coord[0]-Next_Way_Point[0])<=Max_Speed) and
      (abs(Coord[1]-Next_Way_Point[1])<=Max_Speed)) then
     begin
      Coord:=Next_Way_Point;
      Action:=a_none;
      Speed:=0;
     end;
 //Move unit
 if (Action=a_move) then
  Coord:=plus(Coord,Get_Vect(Angle,Speed));

end;
{$ENDREGION}

{$REGION 'Helitank draw'}    
procedure THeliTank.Draw;
var i:byte;
begin
 if tip=ut_Ground then
  Draw_HeliTank((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,height/5,Angle+90,Scale,Cannon_angle,14)
  else
  Draw_HeliTank((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,height/5,Angle+90,Scale,Rotor_ang,7);

 for i:=0 to 2 do   
  if Gaus[i].fire then
   Draw_Gaus(multi(minus(plus(Gaus[i].Coord,multi(Gaus[i].Vector,Gaus[i].Anim)),Camera),Scale),Gaus[i].Angle+90,Scale);
end;       
{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function THeliTank.Enemy_Hit;
begin                
 if tip=ut_Armored then
  Enemy_Hit:=(Gaus[0].Damage*2 div 2) else
 Enemy_Hit:=Gaus[0].Damage*2;
end;
{$ENDREGION}

// ====   Dummy_Platform   ====
{$REGION 'Create Platform'}
constructor TBasePlatform.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
var i:byte;
begin
 tip:=ut_UnderGround;
 Life:=0;
 anim:=0;
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=Crd;
 Action:=a_ready;
 View_Radius:=1000;
 State:=true;
 Color:=$ffdddddd;
 Size:=default_size;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure TBasePlatform.Draw(var Camera:TVector; var Scale:Single);
begin
 Units_tex.DrawRot((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,0,Scale*0.8,Color,8);
end;
{$ENDREGION}

{$REGION 'Move'}
procedure TBasePlatform.Move;
var i:byte;
begin
 Next_Way_Point:=Coord;
end;
{$ENDREGION}

{$REGION 'Fire'}
function TBasePlatform.Fire:boolean;
begin
 Result:=false;
 Fire:=result;
end;
{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function TBasePlatform.Can_Hit(tip:byte):boolean;
begin
 Can_Hit:=false;
end;

function TBasePlatform.Enemy_Hit;
begin
 Enemy_Hit:=0;
end;
{$ENDREGION}
// === Base  ===
{$REGION 'Create Platform'}
constructor TBase.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
var i:byte;
begin
 tip:=ut_Structure;
 Life:=1000;
 anim:=0;
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=Crd;
 Action:=a_none;
 View_Radius:=1000;
 State:=true;
 Color:=$ffdddddd;
 Size:=default_size;
end;
{$ENDREGION}

{$REGION 'Move'}
procedure TBase.Move;
var i:byte;
begin
 if Life<100 then
      Emiter.add_particle(
                  plus(Coord,Vector((Random(30)-15),(Random(30)-15)/2)),
                  Vector((Random(10)-5)/2,(Random(10)-5)/2),
                  random(2),
                  30);
 if life<0 then
  begin
    Action:=a_die;
    for i:=0 to 7 do      
    Emiter.add_particle(
                  plus(Coord,Vector((Random(80)-40),(Random(80)-40)/2)),
                  Vector((Random(10)-5)/2,(Random(10)-5)/2),
                  random(2),
                  30);
  end;
 Next_Way_Point:=Coord;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure TBase.Draw(var Camera:TVector; var Scale:Single);
begin
 Units_tex.DrawRot((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,0,Scale*1.6,Color,15);
end;
{$ENDREGION}

// ====   Gun   ====
{$REGION 'Create Gun'}
constructor TTurret.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
var i:byte;
begin       
 tip:=ut_Structure;
 Max_Life:=600;
 Life:=Max_Life;
 anim:=0;
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=Crd;
 Cannon_Angle:=Ang; // :)
 Need_Cannon_Angle:=Ang;
 Angle_Speed:=4;
 Action:=0;
 View_Radius:=1200;
 Gun:=TGun.Create;
 Gun.reload_timer:=60;
 Gun.reload_time:=0;
 Gun.distanse:=1200;
 Gun.damage:=50;  
  State:=true;

                       
 Color:=$ffffffff;
 Size:=default_size;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure TTurret.Draw(var Camera:TVector; var Scale:Single);
begin
 Draw_Platform((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,Cannon_Angle+90,Scale,Convert_anim,Color,10);
  if Gun.fire then
  Draw_Bullet(multi(minus(plus(Gun.Coord,multi(Gun.Vector,Gun.Anim)),Camera),Scale),Gun.Angle+90,Scale);
end;
{$ENDREGION}

{$REGION 'Move'}
procedure TTurret.Move;
var i:byte;
begin

 if Action>=a_die then inc(anim);
 if (anim>0) and (random(2)=0) then
  if (anim<100) or (random(5)=0) and (anim<300) then
   Emiter.add_particle(Coord,Vector((Random(10)-5)/6,(Random(10)-5)/6),random(2),30);    
 if (anim>=death_anim) then
  begin
   Action:=a_free;
   anim:=to_platform;
  end;

 if Action<a_die then
 if state then
  begin
   if (Convert_anim<252) then Convert_anim:=Convert_anim+4;
  end else
   if (Convert_anim>0) then Convert_anim:=Convert_anim-4;
  if (Convert_anim<10) then
   tip:=ut_UnderGround else
   tip:=ut_Structure;
 if tip=ut_UnderGround  then
  if Life<Max_Life then Life:=Life+1;
 if Action>=a_die then
   if (anim=death_anim-2) then   
     for i:=0 to 30 do
      Emiter.add_particle(
                  plus(Coord,Vector((Random(30)-15),(Random(30)-15)/2)),
                  Vector((Random(10)-5)/2,(Random(10)-5)/2),
                  random(2),
                  30);
 Cannon_Angle:=Head_Rotate(Cannon_Angle,Need_Cannon_Angle,Angle_Speed);
 Next_Way_Point:=Coord;
end;
{$ENDREGION}

{$REGION 'Fire'}
function TTurret.Fire:boolean;
var
 s:single;
 i:byte;
begin Result:=false;
 Need_Cannon_Angle:=Get_A(Coord,Target_Coord);
 //Gun reload
 if (Gun.reload_time>0) then dec(Gun.reload_time);
 //Fire        
 if tip=ut_Structure then
 if (Gun.reload_time=0) then
 if has_target then
//  if (Get_S(Coord,Target_Coord)<Gun.distanse) then
  if (Get_R(Need_Cannon_Angle-Cannon_Angle)<5) then
  begin
    Gun.reload_time:=Gun.reload_timer;
    Gun.Fire:=true;
    Gun.Anim:=0;
    Gun.Angle:=Get_A(Coord,Target_Coord);
    Gun.Coord:=Coord;
    s:=Get_S(Coord,Target_Coord);
    if s>50 then
     Gun.Fly_time:=trunc(s/50) else
    Gun.Fly_time:=trunc(s/10);
    Gun.Vector:=multi(minus(plus(Target_Coord,Vector(Random(20)-10,Random(20)-10)),Coord),1/Gun.Fly_time);
  end;
 //bullet animation and
 if Gun.Fire then inc(Gun.Anim);
 if (Gun.Anim>=Gun.Fly_time+1) then
  begin                  
    Gun.Anim:=0;
    Gun.Fire:=false;
    Result:=true;
  end;
 Fire:=result;
end;

{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function TTurret.Can_Hit(tip:byte):boolean;
begin
 Can_Hit:=(tip<>ut_Air) and (tip<>ut_UnderGround);
end;

function TTurret.Enemy_Hit;
begin
 if tip=ut_Armored then
  Enemy_Hit:=(Gun.Damage*(Random(4)+1) div 2) else
 Enemy_Hit:=Gun.Damage*(Random(4)+1);
end;
{$ENDREGION}

// ====   Gaus   ====
{$REGION 'Create Gaus'}
constructor TGaus.Create(Crd,Vp:TVector;Ang:Single;Unit_Side:byte);
var i:byte;
begin       
 tip:=ut_Structure;
 Max_Life:=600;
 Life:=Max_Life;
 anim:=0;
 Side:=Unit_Side;
 Coord:=Crd;
 Target_Way_Point:=Crd;
 Next_Way_Point:=Crd;
 Cannon_Angle:=Ang; // :)
 Need_Cannon_Angle:=Ang;
 Angle_Speed:=4;
 Action:=0;
 View_Radius:=1200;
 for i:=0 to 3 do
   begin
     Gaus[i]:=TGausgun.Create;
     Gaus[i].reload_timer:=20;
     Gaus[i].reload_time:=Random(Gaus[i].reload_timer);
     Gaus[i].distanse:=1200;
     Gaus[i].damage:=4;
   end;
 State:=true;
 Color:=$ffffffff;
 Size:=default_size;
end;
{$ENDREGION}

{$REGION 'Draw'}
procedure TGaus.Draw(var Camera:TVector; var Scale:Single);
var i:byte;
begin
 Draw_Platform((Coord[0]-Camera[0])*Scale,(Coord[1]-Camera[1])*Scale,Cannon_Angle+90,Scale,Convert_anim,Color,11);
 for i:=0 to 3 do   
  if Gaus[i].fire then
   Draw_Gaus(multi(minus(plus(Gaus[i].Coord,multi(Gaus[i].Vector,Gaus[i].Anim)),Camera),Scale),Gaus[i].Angle+90,Scale);
end;
{$ENDREGION}

{$REGION 'Move'}
procedure TGaus.Move;
var i:byte;
begin

 if Action>=a_die then inc(anim);
 if (anim>0) and (random(2)=0) then
  if (anim<100) or (random(5)=0) and (anim<300) then
   Emiter.add_particle(Coord,Vector((Random(10)-5)/6,(Random(10)-5)/6),random(2),30);    
 if (anim>=death_anim) then 
  begin
   Action:=a_free;
   anim:=to_platform;
  end;

 if Action<a_die then
 if state then
  begin
   if (Convert_anim<252) then Convert_anim:=Convert_anim+4;
  end else
   if (Convert_anim>0) then Convert_anim:=Convert_anim-4;
  if (Convert_anim<10) then
   tip:=ut_UnderGround else
   tip:=ut_Structure;        
 if tip=ut_UnderGround  then
  if Life<Max_Life then Life:=Life+1;

 if Action>=a_die then
   if (anim=death_anim-2) then   
     for i:=0 to 30 do
      Emiter.add_particle(
                  plus(Coord,Vector((Random(30)-15),(Random(30)-15)/2)),
                  Vector((Random(10)-5)/2,(Random(10)-5)/2),
                  random(2),
                  30);
 Cannon_Angle:=Head_Rotate(Cannon_Angle,Need_Cannon_Angle,Angle_Speed);
 Next_Way_Point:=Coord;
end;
{$ENDREGION}

{$REGION 'Fire'}
function TGaus.Fire:boolean;
var
 s:single;
 i:byte;
begin Result:=false;
 Need_Cannon_Angle:=Get_A(Coord,Target_Coord);
 if (Gaus[0].reload_time>0) and (Gaus[0].reload_time=Gaus[1].reload_time) then
  Gaus[0].reload_time:=Gaus[0].reload_time+5;
 if (Gaus[1].reload_time>0) and (Gaus[1].reload_time=Gaus[2].reload_time) then
  Gaus[1].reload_time:=Gaus[1].reload_time+5;

 for i:=0 to 2 do
 begin
 //Rockets reload
 if (Gaus[i].reload_time>0) then dec(Gaus[i].reload_time);
 //Fire          
 if tip=ut_Structure then
 if (Gaus[i].reload_time=0) then
 if has_target then
 // if (Get_S(Coord,Target_Coord)<Gaus[i].distanse) then
//  if (Get_R(Need_Cannon_Angle-Cannon_Angle)<5) then
  begin
    Gaus[i].reload_time:=Gaus[i].reload_timer;
    Gaus[i].Fire:=true;
    Gaus[i].Anim:=0;
    Gaus[i].Angle:=Get_A(Coord,Target_Coord);
    Gaus[i].Coord:=Coord;
    s:=Get_S(Coord,Target_Coord);
    if s>50 then
     Gaus[i].Fly_time:=trunc(s/50) else
    Gaus[i].Fly_time:=trunc(s/10);
    Gaus[i].Vector:=multi(minus(plus(Target_Coord,Vector(Random(20)-10,Random(20)-10)),Coord),1/Gaus[i].Fly_time);
  end;
 //bullet animation and
 if Gaus[i].Fire then inc(Gaus[i].Anim);
 if (Gaus[i].Anim>=Gaus[i].Fly_time+1) then
  begin                  
    Gaus[i].Anim:=0;
    Gaus[i].Fire:=false;
    Result:=true;
  end;
 end;
 Fire:=result;
end;

{$ENDREGION}

{$REGION 'Damage function return damage of the hitting'}
function TGaus.Can_Hit(tip:byte):boolean;
begin
 Can_Hit:=(tip=ut_Air);
end;

function TGaus.Enemy_Hit;
begin
 if tip=ut_Armored then
  Enemy_Hit:=(Gaus[0].Damage div 2) else
 Enemy_Hit:=Gaus[0].Damage;
end;
{$ENDREGION}


end.
