unit game_objects;

interface
uses
CoreX, i2_3d, dgSoundEngine, classes;

type
TAppearance = ( dLeft, dRight, dTop, dBottom );

TCollisionTri = array[0..2] of tvec3f;

TGameObject = class
  Collision : array of TCollisionTri;
//  Health : Integer;
  Visible : Boolean;
  Matrix : TMat4f;
  Color : TVec3f;
  Friendly : Boolean;
  procedure SetCol(fID : Integer; fA, fB, fC : TVec3f );
  constructor Create; overload; virtual;
  procedure Free; virtual;
  procedure Visualize;
  procedure Update; virtual; abstract;
  procedure Render; virtual; abstract;
end;

TSubEvent = class;

TSubSpawner = Class
  Completed : Boolean;
  TimeOffsetCondition : real;//если время подсобытия больше этого, то запуск
  EnemyType : byte;//1,2,3,4,255
  Appearance : TAppearance;
  ScreenFactor : byte;
  Homing : Boolean;
  Tactic : byte;
  constructor Create(fsubevent : TSubEvent);
  Constructor Add( fTOC : real; fET : byte; fA : TAppearance; fSF : byte; fH : boolean; fT : Byte; fsubevent : TSubEvent);
  Procedure Execute;
end;

TSubEvent = class
  TimeCondition : real;//если время в игре больше этого, то запуск
  EnemyCondition : Integer;//если врагов меньше чем это, то запуск
  Started : Boolean;
  Credits : Boolean;
  TimeOffset : real;
  Spawners : array of TSubSpawner;
  Constructor Add;
  Function LastSubSpawner : TSubSpawner;
  Function Completed : Boolean;
end;

TEnemySpawner = class( TGameObject )
  Events : array of TSubEvent;
  CurrentEvent : integer;
  Time : real;
  procedure Load( fFileName : String );
  procedure Free; override;
  function LastEvent : TSubEvent;
  function LastSpawner : TSubSpawner;
  function CurrEvent: TSubEvent;
  procedure Update; override;
  procedure Render; override;
end;

TGameControler = object
  Enemies : Integer;
  Count : Integer;
  Objects : array of TGameObject;
  Function Add( fObj : TGameObject ) : Integer;
  Function Del( fObj : TGameObject ) : Boolean;
  Function Get( fID : Integer ) : TGameObject;
  Function Kill : boolean;
  Procedure Update;
end;

TTurret = class;

TPlayerShip = class( TGameObject )
  Continues : Integer;
  Score : Integer;
  GunUpgrade : Integer;
  Guns : Array[0..3] of TTurret;
  Invincibility : real;
  InvMax : real;
  Health : integer;
  LeftRocket : Boolean;
  gunDelay : real;
  rockDelay : real;
  gunLoader : real;
  rockLoader : real;
  rockClip : integer;
  rockClipMax : integer;
  rockClipLoader : real;
  rockClipDelay : real;
  rockEngaged : Boolean;
  constructor Create; override;
  procedure Update; override;
  procedure Render; override;
end;

TRocket = class( TGameObject )
  Damage : Integer;
  Speed : Real;
  MaxSpeed : Real;
  Friendly : Boolean;
  XOffSet : real;
  XTarget : real;
  constructor Create( fMatrix : TMat4f;  fFriendly : Boolean; fDamage : Integer; fXOffSet : real;  fXTarget : real = 0 ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
end;

TBullet = class( TGameObject )
  Damage : Integer;
  Speed : Real;
  MaxSpeed : Real;
  Friendly : Boolean;
  XOffSet : real;
  XTarget : real;
  constructor Create( fMatrix : tmat4f; fFriendly : Boolean; fDamage : Integer; fXOffset : real ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
end;

TTurret = class( TGameObject )
  Enabled : Boolean;
  Fixed : Boolean;
  FixedAngle : real;
  RocketGun : Boolean;//ракеты/пули
  Master : TGameObject;
  Offset : tvec3f;
  Damage : integer;
  Friendly : boolean;
  gunDelay : real;
  gunLoad : real;
  gunClipLoad : real;
  gunClipDelay : real;
  gunClipSize : Integer;
  gunClip : Integer;

  procedure Update; Override;
end;

TGatlingTurret = class( TTurret )
//  Enabled : boolean;
  Spin : real;
//  Fixed : Boolean;
  constructor Create( fDamage : Integer; fMaster : TGameObject; fOffset : tvec3f; fFixed : Boolean; fVisible : Boolean = true ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
end;

TDoubleTurret = class( TTurret )
  LeftBarrel : boolean;
  RecoilL, RecoilR : real;
//  Enabled : boolean;
//  Fixed : Boolean;
  constructor Create(  fDamage : Integer; fMaster : TGameObject; fOffset : tvec3f; fFixed : Boolean; fLeftBarrel : boolean = false; fVisible : Boolean = true ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
end;

TEnemy = class( TGameObject )
  Reward : Integer;
  Tactic : Integer;
  TacticTicker : real;
  Health : Integer;
  Damage : Integer;
  Speed : Real;
  Homing : Boolean;
  Spin : Real;
  constructor Create( fAppearance : TAppearance; fScreenFactor : real; fHoming : Boolean = false ); reintroduce; overload;
  procedure Setup; virtual; abstract;
//  procedure Update; override;
//  procedure Render; override
end;

TEnemy1 = class( TEnemy )
//  constructor Create( fMatrix : tmat4f ); reintroduce; overload;
//  constructor Create( fAppearance : TAppearance; fScreenFactor : real; fHoming : Boolean = false ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TEnemy2 = class( TEnemy )
//  constructor Create( fAppearance : TAppearance; fScreenFactor : real; fHoming : Boolean = false ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TEnemy3 = class( TEnemy )
//  constructor Create( fAppearance : TAppearance; fScreenFactor : real; fHoming : Boolean = false ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TBomb = class( TEnemy )
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TTerrain = class( TGameObject )
  mat1, mat2 : tmat4f;
  constructor Create; override;
  procedure Update; override;
  procedure Render; override;
end;

TBossPart = class;

TBoss = class ( TEnemy )
  MoveLeft : Boolean;
  Guns : Array of TTurret;
  Parts : Array of TBossPart;
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
  procedure Free; override;
end;

TBossPart = class ( TEnemy )
//  Health : Integer;
  Master : TBoss;
  Model : tMeshContainer;
  Offset : tvec3f;
  Constructor Create( fMaster : tboss; fOffset : tvec3f ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
//  procedure Setup; virtual;
end;

TBossRotor = class ( TBossPart )
  Spin : real;
  procedure Update; override;
  procedure Render; reintroduce; override;
  procedure Setup; override;
end;

TBossCoverL = class ( TBossPart )
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TBossCoverR = class ( TBossPart )
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TBossHead = class ( TBossPart )
  procedure Update; override;
  procedure Render; override;
  procedure Setup; override;
end;

TWreckage = class( TGameObject )
  Model : tMeshContainer;
  SpinY, SpinZ : real;
  constructor Create( fMat : TMat4f; fModel : tMeshContainer; fColor : TVec3f ); reintroduce; overload;
  procedure Update; override;
  procedure Render; override;
end;

var
GC : TGameControler;
ES : TEnemySpawner;

implementation
uses main;

{ TGameObject }
{$region}
constructor TGameObject.Create;
begin
  Visible := true;
  gc.Add( self );
  matrix.identity;
end;

procedure TGameObject.Free;
begin
  if gc.Del( self ) then
  begin
  if self is tenemy then dec( gc.Enemies );
    Collision := nil;
    Destroy;
  end;
end;

procedure TGameObject.SetCol(fID : Integer; fA, fB, fC: TVec3f);
begin
  Collision[fID][0] := fA;
  Collision[fID][1] := fB;
  Collision[fID][2] := fC;
end;

procedure TGameObject.Visualize;
begin
if Visible then Update;

end;

{$endregion}
{ TGameControler }
{$region}
function TGameControler.Add(fObj: TGameObject): Integer;
begin
  count := count + 1;
  if length( Objects ) < count  then
    setlength( Objects, count );

  Objects[ count - 1 ] := fObj;
  result := count - 1;
end;

function TGameControler.Del(fObj: TGameObject): Boolean;
var
  I: Integer;
begin
  result := false;

  for I := 0 to Count - 1 do
    if get( I ) = fObj then
      begin
          Count := Count - 1;
          Objects[ i ] := Objects[ count ];
          result := true;
          exit;
      end;

end;

function TGameControler.Get(fID: Integer): TGameObject;
begin
  result := Objects[ fID ];
end;

function TGameControler.Kill: boolean;
var
  I: Integer;
begin
for I := 0 to count - 1 do
begin
  Objects[i].Destroy;
end;
Objects := nil;
result := true;
end;

procedure TGameControler.Update;
var
  I: Integer;
begin
for I := 0 to Count - 1 do
Objects[i].Visualize;
end;
{$endregion}
{ TPlayerShip }

constructor TPlayerShip.Create;
var
  I: Integer;
begin
  inherited;
  invmax := 0.5;
  SetLength( Collision, 2 );
  setcol( 0,vec3f( -4.4, 0, -13 ),vec3f( 4.4, 0, 15 ),vec3f( -4.4, 0, 15 ));
  setcol( 1,vec3f( 4.4, 0, 15 ),vec3f( 4.4, 0, -13 ),vec3f( -4.4, 0, -13 ));

  GunUpgrade := 0;
  gunDelay := 0.25;
  rockDelay := 0.1;
  rockClipDelay := 5;
  rockClipMax := 6;
  rockClip := 0;
  Color := vec3f( 0.1, 0.65, 0.2 );
  health := 100;
  matrix.Pos := vec3f( 0, 0, 170 );

  Guns[0] := TGatlingTurret.Create( 15, self, vec3f(5,0,0), true, false );
  Guns[1] := TGatlingTurret.Create( 15, self, vec3f(-5,0,0), true, false );
  Guns[2] := TDoubleTurret.Create( 20, self, vec3f(12,0,12), false, false, false );
  Guns[3] := TDoubleTurret.Create( 20, self, vec3f(-12,0,12), false, true, false );

  for I := 0 to 3 do
    Guns[i].Friendly := true;

end;

procedure TPlayerShip.Render;
{var
  I: Integer;}
begin
  gl.Color3fv( Color );
 ship.Draw;

{gl.PushMatrix;
gl.MultMatrixf(matrix);
for I := 0 to length( collision ) - 1 do
begin
   gl.Beginp( GL_LINE_LOOP );
    gl.Vertex3fv( Collision[i][0]);
    gl.Vertex3fv( Collision[i][1]);
    gl.Vertex3fv( Collision[i][2]);
    gl.Vertex3fv( Collision[i][0]);
  gl.Endp;
end;
gl.PopMatrix;
}
end;

procedure TPlayerShip.Update;
var
xo : real;
fireGat, fireHeavy, fireRocket : Boolean;
begin
  with input, mouse, matrix do
  begin

    if Health <= 0 then
    begin
      ShakeCam(10);
      Continues := Continues + 1;
      dgsoundengine.Sounds['death'].play();
      Health := 100;
      Invincibility := 1.5;
    end;

    pos := vec3f( clamp( pos.x + joy.Axis.X * 5 * corex.Render.DeltaTime + Delta.X, -245, 245 ), 0, clamp( pos.z + Joy.Axis.Y * 5 * corex.Render.DeltaTime + Delta.Y, -170, 170 ) );
//    matrix.Rot := quat( clamp( delta.x*deg2rad, -pi/8, pi/8 ), vec3f(0,1,0));
    Rotate( -clamp(delta.x + joy.Axis.X * 5 * corex.Render.DeltaTime, -10, 10)  * deg2rad, vec3f( 0, 1, 0 ));
    Rot := matrix.rot.Lerp(quat(0,vec3f(0,1,0)), 25*corex.Render.DeltaTime);
    gunLoader := gunLoader - corex.Render.DeltaTime;
    rockLoader := rockLoader - corex.Render.DeltaTime;
    rockClipLoader := rockClipLoader - corex.Render.DeltaTime;
    Invincibility := Invincibility - corex.Render.DeltaTime;

    if ( gunLoader <= 0 )  then
    begin
      with TBullet.Create( self.Matrix, true, 5, 0 ) do
      begin
        Color := vec3f( 1, 1, 0);
      end;
    gunLoader := gunDelay;
    end;  

    if GunUpgrade = 0 then
    begin
      gunDelay := 0.25;
      Guns[0].Visible := false;
      Guns[1].Visible := false;
      Guns[2].Visible := false;
      Guns[3].Visible := false;
    end;

if GunUpgrade = 1 then
    begin
      gunDelay := 0.15;
      Guns[0].Visible := false;
      Guns[1].Visible := false;
      Guns[2].Visible := false;
      Guns[3].Visible := false;
    end;

    if GunUpgrade>1 then
    begin
      gunDelay := 0.15;
      Guns[0].Visible := true;
      Guns[1].Visible := true;
      Guns[2].Visible := false;
      Guns[3].Visible := false;
    end;

    if GunUpgrade>2 then
    begin
      Guns[0].Visible := true;
      Guns[1].Visible := true;
      Guns[2].Visible := true;
      Guns[3].Visible := true;
    end;

    fireGat := ( down[ikMouseL] ) or ( down[ikJoy3] );
    Guns[0].Enabled := fireGat;
    Guns[1].Enabled := fireGat;

    fireHeavy := ( down[ikMouseR] ) or ( down[ikJoy4] );
    Guns[2].Enabled := fireHeavy;
    Guns[3].Enabled := fireHeavy;

    fireRocket := ( down[ikMouseM] ) or ( down[ikSpace] ) or ( down[ikJoy2] );
    if ( fireRocket ) and ( rockClip < rockClipMax )  and ( rockClipLoader <= 0 ) then rockEngaged := true;

    if ( rockEngaged ) and ( rockLoader <= 0 ) then
    begin
        if LeftRocket then XO := -10 else XO := +10;
      with TRocket.Create( self.Matrix, true, 25, xo, matrix.Pos.x )  do
      begin
 //       matrix.Rot := self.Matrix.Rot;
      end;
    LeftRocket := not LeftRocket;
    rockLoader := rockDelay;
    rockClip := rockClip + 1;
      if rockClip >= rockClipMax then
      begin
        rockEngaged := false;
        rockClip := 0;
        rockClipLoader := rockClipDelay;
      end;
    end;

    if down[ikQ] then Rotate( 45* deg2rad * corex.render.DeltaTime, vec3f( 0, 1, 0 ));
    if down[ikE] then Rotate( -45* deg2rad * corex.render.DeltaTime, vec3f( 0, 1, 0 ));
    if hit[ik1] then GunUpgrade := 1;
    if hit[ik2] then GunUpgrade := 2;
    if hit[ik3] then GunUpgrade := 3;
    if hit[ik4] then GunUpgrade := 0;
  end;
  Color := Color.Lerp( vec3f( 0.1, 0.65, 0.2 ), 0.95 * corex.Render.DeltaTime );
  ship.matrix := matrix;
  render;
end;

{ TRocket }

constructor TRocket.Create( fMatrix : TMat4f;  fFriendly : Boolean; fDamage : Integer; fXOffSet : real;  fXTarget : real = 0 );
begin
  inherited Create;
  XOffSet := fXOffSet;
  Friendly := fFriendly;
  Damage := fDamage;
  Matrix := fMatrix;
//  with Matrix do
//    Pos := Pos + vec3f( e00, e10, e20 ) * xoffset;
  XTarget := fXTarget;
  if friendly then color := vec3f( 0, 0.6, 0.4 ) else color := vec3f( 0.6, 0, 0 );
  speed := 0;
  maxspeed := 1000;
end;

procedure TRocket.Render;
begin
  gl.Color3fv( Color );
  rocket.Draw;
end;

procedure TRocket.Update;
var
I,J : Integer;
begin

Speed := lerp( speed, maxspeed, 0.85 * corex.render.deltatime );
xoffset := lerp( xoffset, 0, 0.95 * corex.Render.DeltaTime );
with matrix, pos do
begin
//pos :=  vec3f( xoffset, y, z );
Pos := pos + vec3f( e02, e12, e22 ) * -speed * corex.Render.DeltaTime;
//matrix.Rotate( deg2rad * 720 * corex.render.DeltaTime, vec3f( 0, 0, 1));
//vec3f( xoffset, 0, pos.z-speed * corex.render.deltatime);
//Pos := vec3f( offset, y, z );



//matrix.Rotate( deg2rad * 720 * corex.Render.DeltaTime, vec3f( 0, 0, 1) );

if ( z < -200 ) or ( z > 200 )
or ( x < -250 ) or ( x > 250 ) then Free;

    if (pos.Dist(player.Matrix.Pos) < 10) and ( not Friendly ) then
    begin
      if player.Invincibility < 0 then
      begin
        dec( player.Health, Damage );
        dgsoundengine.Sounds['plr_hit'].play();
        (player).Color := vec3f( 1, 1, 1 );
        player.Invincibility := player.invmax;
      end;
      self.Free;
    end;


if Friendly then
    for I := 0 to gc.Count - 1 do
    begin
      if ( gc.Get(i) is TEnemy ) then
      if tenemy(gc.Get(i)).health > 0 then
      begin
        with gc.Get(i) as TEnemy do
        begin
        if (pos.Dist(gc.Get(i).Matrix.Pos)<105) then
          for J := 0 to system.length( gc.Get(i).Collision ) - 1 do
          if TriIntersect( gc.Get(i).Matrix,
                          gc.Get(i).Collision[j][0],
                          gc.Get(i).Collision[j][1],
                          gc.Get(i).Collision[j][2],
                          self.Matrix.Pos ) then
          begin
            Color := vec3f( 1, 1, 1 );
            Health := Health - self.Damage;
            if not (gc.Get(i) is tboss ) then Hit;

            self.free;
            exit;

          end;
        end;

      end;

    end;

end;
  rocket.matrix := matrix;
  with rocket.matrix do
  rocket.matrix.Pos := rocket.matrix.Pos + vec3f( e00, e10, e20 ) * xoffset;

render;
end;

{ TBullet }

constructor TBullet.Create( fMatrix : tmat4f; fFriendly : Boolean; fDamage : Integer; fXOffset : real );
begin
  inherited Create;

  Inc(bulleter);
  if bulleter > 5 then bulleter := 0;

  dgSoundEngine.Sounds['shot'+conv(bulleter)].Play(false);

  Damage := fDamage;
  matrix := fMatrix;
  Friendly := fFriendly;
  color := vec3f( 1,0.5,0 );
  speed := 500;
  maxspeed := 500;
  XOffSet := fXOffset;
end;

procedure TBullet.Render;
begin
  gl.color3fv(color);
  bullet.Draw;
end;

procedure TBullet.Update;
var
  I, J: Integer;
begin
  with matrix, pos do
  begin
  Pos := pos + vec3f( e00, e10, e20 ) * xoffset + vec3f( e02, e12, e22 ) * -speed * corex.render.deltatime;// vec3f( x, 0, z-speed * corex.render.deltatime);
  XOffSet := 0;

  if ( z < -200 ) or ( z > 200 )
  or ( x < -250 ) or ( x > 250 ) then Free;

    if (pos.Dist(player.Matrix.Pos) < 10) and ( not Friendly ) then
    begin
      if player.Invincibility < 0 then
      begin
        dec( player.Health, Damage );
        dgsoundengine.Sounds['plr_hit'].play(false);
        (player).Color := vec3f( 1, 1, 1 );
        player.Invincibility := player.InvMax;
      end;
      self.Free;
    end;

    if Friendly then
    for I := 0 to gc.Count - 1 do
    begin
      if ( gc.Get(i) is TEnemy ) then
      if tenemy(gc.Get(i)).health > 0 then
      begin
        with gc.Get(i) as TEnemy do
        begin
        if (pos.Dist(gc.Get(i).Matrix.Pos)<105) then
          for J := 0 to system.length( gc.Get(i).Collision ) - 1 do
          if TriIntersect( gc.Get(i).Matrix,
                          gc.Get(i).Collision[j][0],
                          gc.Get(i).Collision[j][1],
                          gc.Get(i).Collision[j][2],
                          self.Matrix.Pos ) then
          begin
            Color := vec3f( 1, 1, 1 );
            Health := Health - self.Damage;
            if not (gc.Get(i) is tboss ) then Hit;
            self.free;
            exit;

          end;
        end;

      end;

    end;

  end;

  bullet.matrix := matrix;
  render;
end;

{ TGatlingTurret }

constructor TGatlingTurret.Create( fDamage : Integer; fMaster : TGameObject; fOffset : tvec3f; fFixed : Boolean; fVisible : Boolean = true ); 
begin
  inherited Create;

  if fMaster <> nil then Master := fMaster;

  if fMaster = player then Friendly := true;

  Damage := fDamage;

  color := vec3f( 0.8,0.25,0 );

  offset := foffset;
  Fixed := fFixed;
  Visible := fVisible;
  enabled := true;
  gunDelay := 0.075;
  gunClipDelay := 2;
  gunClipSize := 20;
end;

procedure TGatlingTurret.Render;
begin

  gl.Color3fv( color );
  turretG.Draw;
  turretheadG.Draw;
end;

procedure TGatlingTurret.Update;
var
d, r : tvec3f;
begin
  inherited;
    gunLoad := gunLoad - corex.render.DeltaTime;
    gunClipLoad := gunClipLoad - corex.render.DeltaTime;

  if Enabled then
  begin

with matrix do
  if (not friendly) and (not fixed) then
    begin
      d := (pos - player.Matrix.Pos).normal;
      r := d.Cross(vec3f(0,1,0)).Normal;
      e02 := d.x;
      e12 := d.y;
      e22 := d.z;

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;
  end;

    if gunClip >= gunClipSize  then
    begin
      gunClip := 0;
      gunClipLoad := gunClipDelay;
    end;

    if ( gunClipLoad <= 0 ) then
    begin
      spin := spin + 320;//lerp( spin, 720, 0.5{ * corex.render.DeltaTime} );

      if ( gunLoad <= 0 ) then
      begin
        gunLoad := gunDelay;
        gunClip := gunClip + 1;
       if not RocketGun then
        with TBullet.Create( matrix, friendly, damage, 0 ) do
        begin
          color := vec3f( 0.8,0.25,0 );
        end
        else
        with TRocket.Create( matrix, friendly, damage, 0 ) do
        begin
          color := vec3f( 0.8,0.25,0 );
        end
      end;
    end
    else
    begin

    end;

  end else spin := 0;

with master.matrix, pos do
begin
  if fixed then Matrix.Rot := Master.Matrix.Rot;
  matrix.Pos := Master.Matrix.Pos + vec3f( e00, e10, e20 ) * offset.x + vec3f( e02, e12, e22 ) * offset.z;
end;

  Matrix.Rotate( FixedAngle, vec3f( 0, 1, 0 ) );

  turretHeadG.matrix := matrix;

  turretG.matrix := matrix;
  turretG.matrix.Rotate( deg2rad * spin * corex.Render.DeltaTime, vec3f( 0, 0, 1 ) );

  render;
end;

{ TDoubleTurret }

constructor TDoubleTurret.Create(  fDamage : Integer; fMaster : TGameObject; fOffset : tvec3f; fFixed : Boolean; fLeftBarrel : boolean = false; fVisible : Boolean = true );
begin
  inherited Create;

  Damage := fDamage;
  color := vec3f( 1,0.5,0 );

  if fMaster <> nil then Master := fMaster;

  if fMaster = player then Friendly := true;

  offset := foffset;
  Fixed := fFixed;
  Visible := fVisible;
  LeftBarrel := fLeftBarrel;
  enabled := true;
  gunDelay := 0.2;
  gunClipDelay := 2;
  gunClipSize := 10;

end;

procedure TDoubleTurret.Render;
begin
  gl.Color3fv( color );
  turretheadR.Draw;
  turretRL.Draw;
  turretRR.Draw;
end;

procedure TDoubleTurret.Update;
var
xo : integer;
d, r : tvec3f;
begin
  inherited;
    gunLoad := gunLoad - corex.render.DeltaTime;
    gunClipLoad := gunClipLoad - corex.render.DeltaTime;

  if Enabled then
  begin

  with matrix do
  if (not friendly) and (not fixed) then
    begin
      d := (pos - player.Matrix.Pos).normal;
      r := d.Cross(vec3f(0,1,0)).Normal;
      e02 := d.x;
      e12 := d.y;
      e22 := d.z;

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;
  end;

    if gunClip >= gunClipSize  then
    begin
      gunClip := 0;
      gunClipLoad := gunClipDelay;
    end;

    if ( gunClipLoad <= 0 ) then
    begin



      if ( gunLoad <= 0 ) then
      begin
        if LeftBarrel then
        begin
         xo := 2;
         RecoilR := -4;
        end
        else
        begin
          xo := -2;
          RecoilL := -4;
        end;
      
        LeftBarrel := not LeftBarrel;
        gunLoad := gunDelay;
        gunClip := gunClip + 1;
        if not RocketGun then
        TBullet.Create( matrix, friendly, damage, xo )
        else
        TRocket.Create( matrix, friendly, damage, xo );
      end;
    end
    else
    begin

    end;

  end;

with master.matrix, pos do
begin
  if fixed then Matrix.Rot := Master.Matrix.Rot;
  matrix.Pos := Master.Matrix.Pos + vec3f( e00, e10, e20 ) * offset.x + vec3f( e02, e12, e22 ) * offset.z;
end;

  Matrix.Rotate( FixedAngle, vec3f( 0, 1, 0 ) );
  turretHeadR.matrix := matrix;

with matrix do
begin
  turretRR.matrix := matrix;
  turretRR.matrix.Pos := Pos - vec3f( e02, e12, e22 ) * recoilR;

  turretRL.matrix := matrix;
  turretRL.matrix.Pos := Pos - vec3f( e02, e12, e22 ) * recoilL;
end;
  RecoilL := lerp( RecoilL, 0, 1.95 * corex.Render.DeltaTime );
  RecoilR := lerp( RecoilR, 0, 1.95 * corex.Render.DeltaTime );
//  turretG.matrix.Rotate( deg2rad * 360 * corex.Render.DeltaTime, vec3f( 0, 0, 1 ) );


  render;
end;

{ TEnemy1 }

{constructor TEnemy1.Create( fMatrix : tmat4f );
begin
  inherited Create;
  matrix := fmatrix;
end;}

procedure TEnemy1.Render;
begin
  gl.Color3fv( color );
  enemy1.Draw;
end;

procedure TEnemy1.Setup;
begin
  Reward := 5;

  SetLength( Collision, 5 );
  Collision[0][0] := vec3f( 0, 0, -5.2 );
  Collision[0][1] := vec3f( 12, 0, -1.2 );
  Collision[0][2] := vec3f( -12, 0, -1.2 );

  Collision[1][0] := vec3f( -12, 0, -1.2 );
  Collision[1][1] := vec3f( 12, 0, -1.2 );
  Collision[1][2] := vec3f( 16, 0, 7 );

  Collision[2][0] := vec3f( 16, 0, 7 );
  Collision[2][1] := vec3f( -16, 0, 7 );
  Collision[2][2] := vec3f( -12, 0, -1.2 );

  Collision[3][0] := vec3f( 12, 0, -1.2 );
  Collision[3][1] := vec3f( 17.5, 0, 2.5 );
  Collision[3][2] := vec3f( 18.7, 0, 12.6 );

  Collision[4][0] := vec3f( -12, 0, -1.2 );
  Collision[4][1] := vec3f( -17.5, 0, 2.5 );
  Collision[4][2] := vec3f( -18.7, 0, 12.6 );

//  Health := random( 3 ) + 3;
  Health := random( 8 ) + 8;
  Speed := 150;
  Damage := 7 + random( 6 );
  color := vec3f( 0.75, 0.1, 0.1 );
end;

procedure TEnemy1.Update;
var
d, r : tvec3f;
I : Integer;
m : TMat4f;
begin
  if Health <= 0 then
  begin
    inc( player.Score, reward ); 
    Boom;
    TWreckage.Create(matrix,enemy1,color);
    free;
    exit;
  end;
  
  TacticTicker := TacticTicker + corex.Render.DeltaTime;

  case Tactic of
  0 : if TacticTicker > 0.5 then  //speed up
      speed := lerp( speed, 600, 0.9 * corex.Render.DeltaTime );

  1 : if TacticTicker >= 1.5 then //accuracy shot
  begin
    TacticTicker := 0;

    m := matrix;

    with m, pos do
    begin
      d := (pos - player.Matrix.Pos).Normal;
      r := d.Cross(vec3f(0,1,0));

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;

      e02 := d.x;
      e12 := d.y;
      e22 := d.z;
    end;


    TBullet.Create(m,false,damage,0);
  end;
  2 : if TacticTicker >= 1 then //front shot
  begin
    TacticTicker := 0;
    TBullet.Create(matrix,false,damage,0);
    if Homing then homing := false;
    Tactic := trunc(random(2) * 2);
  end;
       
  end;

  with matrix, pos do
  begin
  if homing then
    begin
      d := (pos - player.Matrix.Pos).normal;
      r := d.Cross(vec3f({e10,e11,e12}0,1,0)).Normal;
      e02 := d.x;
      e12 := d.y;
      e22 := d.z;

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;
  end;

    Pos := pos + vec3f( e02, e12, e22 ) * -speed * corex.Render.DeltaTime;

    if ( z < -225 ) or ( Matrix.Pos.z > 225 )
    or ( x < -300 ) or ( Matrix.Pos.x > 300 ) then Free;

    for I := 0 to system.length( collision ) - 1 do
      if TriIntersect( matrix, collision[i][0],collision[i][1],collision[i][2],player.matrix.pos) then
      begin
        if player.Invincibility < 0 then
        begin
          dec( player.Health, 7 + random( 6 ) - 3 );
          dgsoundengine.Sounds['plr_hit'].play();
          (player).Color := vec3f( 1, 1, 1 );
          player.Invincibility := player.InvMax;
        end;
        health := -1;
        break;
      end;
//     begin color := vec3f( 1, 1, 1 ) end;
//    if pos.Dist(gc.Get(0).Matrix.Pos) < 10 then Free;

  end;

  enemy1.matrix := matrix;

  color := color.Lerp( vec3f( 0.75, 0.1, 0.1 ), 0.95 * corex.render.deltatime );

  render;
end;

{ TTerrain }
{$region 'terrain'}
constructor TTerrain.Create;
begin
  inherited;
  mat1.Identity;
  mat1.Pos := vec3f( 0, -75, -500);
  mat2.Identity;
  mat2.Pos := vec3f( 0, -75, 0 );
  color := vec3f( 0.75, 0.75, 0.75 );
end;

procedure TTerrain.Render;
begin
  gl.Color3fv( color );
  terrain.matrix := mat1;
  terrain.Draw;
  terrain.matrix := mat2;
  terrain.Draw;

end;

procedure TTerrain.Update;
begin

  mat1.Pos := mat1.pos + vec3f( 0, 0, ScrollSpeed * corex.render.deltatime );
  mat2.Pos := mat2.pos + vec3f( 0, 0, ScrollSpeed * corex.render.deltatime );
with mat1, Pos do
  if z >= 500 then pos := vec3f( x, y, mat2.Pos.z - 500 );
with mat2, Pos do
  if z >= 500 then pos := vec3f( x, y, mat1.Pos.z - 500 );

render;
end;
{$endregion}
{ TEnemy2 } //twister

procedure TEnemy2.Render;
begin
  gl.Color3fv( color );
  enemy2.Draw;
end;

procedure TEnemy2.Setup;
begin
  Reward := 20;

  SetLength( Collision, 3 );
  Collision[0][0] := vec3f( -8, 0, -10 );
  Collision[0][1] := vec3f( 9.5, 0, 26 );
  Collision[0][2] := vec3f( -9.5, 0, 26 );

  Collision[1][0] := vec3f( 9.5, 0, 26 );
  Collision[1][1] := vec3f( 8, 0, -10 );
  Collision[1][2] := vec3f( -8, 0, -10 );

  Collision[2][0] := vec3f( 0, 0, -16 );
  Collision[2][1] := vec3f( 8, 0, -10 );
  Collision[2][2] := vec3f( -8, 0, -10 );
  
//  Health := random( 6 ) + 6;
  Health := random( 15 ) + 15;
  Speed := 300;
  Damage := 7 + random( 6 );
  color := vec3f( 0.1, 0.3, 0.2 );
end;

procedure TEnemy2.Update;
var
d, r : tvec3f;
  I: Integer;
begin
  if Health <= 0 then
  begin
    inc( player.Score, reward );
    Boom;
    TWreckage.Create(matrix,enemy2,color);
    free;
    exit;
  end;

  TacticTicker := TacticTicker + corex.Render.DeltaTime;

  case Tactic of
  0 : if homing then if TacticTicker > 0.5 then Tactic := 1;
  1 :
  begin
    homing := false;
    speed := lerp( speed, 650, 0.9 * corex.Render.DeltaTime );
  end;

  end;
  

  with matrix, pos do
  begin
  if homing then
    begin
      d := (pos - player.Matrix.Pos).normal;
      r := d.Cross(vec3f(0,1,0)).Normal;
      e02 := d.x;
      e12 := d.y;
      e22 := d.z;

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;
  end;

    matrix.Pos := matrix.Pos + vec3f( e02, e12, e22 ) * -speed * corex.Render.DeltaTime;

    if ( z < -225 ) or ( Matrix.Pos.z > 225 )
    or ( x < -300 ) or ( Matrix.Pos.x > 300 ) then begin Free;end;

    for I := 0 to system.length( collision ) - 1 do
      if TriIntersect( matrix, collision[i][0],collision[i][1],collision[i][2],player.matrix.pos) then
      begin
      if player.Invincibility < 0 then
        begin
          dec( player.Health, 17 + random( 6 ) - 3 );
          dgsoundengine.Sounds['plr_hit'].play();
          (player).Color := vec3f( 1, 1, 1 );
          player.Invincibility := player.InvMax;
        end;
        health := -1;
        break;
      end;

//     begin color := vec3f( 1, 1, 1 ) end;
//     if pos.Dist(gc.Get(0).Matrix.Pos) < 10 then Free;
  end;

  Spin := Spin + 720 * deg2rad * corex.Render.DeltaTime ;

  enemy2.matrix := matrix;
  enemy2.matrix.Rotate( -spin, vec3f( 0, 0, 1 ) );

  color := color.Lerp( vec3f( 0.1, 0.3, 0.2 ), 0.95 * corex.render.deltatime );

  render;
end;

{ TEnemy }

constructor TEnemy.Create(fAppearance: TAppearance; fScreenFactor: real;
  fHoming: Boolean);
var
  tmpos : tvec3f;
begin
  inherited Create;

  inc( gc.Enemies );

  Tactic := 0;

  Homing := fHoming;
  with matrix, pos do
  begin
    Identity;
    case fAppearance of
      dLeft:
      begin
        tmpos := vec3f( -275, 0, 340 / 100 * fScreenFactor - 170 );
        e00 := 0; e10 := 0; e20 := -1;
        e01 := 0; e11 := 1; e21 := 0;
        e02 := -1; e12 := 0; e22 := 0;
        pos := tmpos;
      end;
      dRight:
      begin
        tmpos := vec3f( 275, 0, 340 / 100 * fScreenFactor - 170 );
        e00 := 0; e10 := 0; e20 := -1;
        e01 := 0; e11 := 1; e21 := 0;
        e02 := +1; e12 := 0; e22 := 0;
        pos := tmpos;
      end;
      dTop:
      begin
        tmpos := vec3f( 490 / 100 * fScreenFactor - 245, 0, -200  );
        e00 := 1; e10 := 0; e20 := 0;
        e01 := 0; e11 := 1; e21 := 0;
        e02 := 0; e12 := 0; e22 := -1;
        pos := tmpos;
      end;
      dBottom:
      begin
        tmpos := vec3f( 490 / 100 * fScreenFactor - 245, 0, 200  );
        e00 := 1; e10 := 0; e20 := 0;
        e01 := 0; e11 := 1; e21 := 0;
        e02 := 0; e12 := 0; e22 := 1;
        pos := tmpos;
      end;

    end;
  end;

  setup;
end;
{ TEnemy3 } //blue

procedure TEnemy3.Render;
begin
  gl.Color3fv( Color );
  enemy3.Draw;
end;

procedure TEnemy3.Setup;
begin
  Reward := 10;

  SetLength( Collision, 2 );
  Collision[0][0] := vec3f( 8.5, 0, -17.5 );
  Collision[0][1] := vec3f( -8.5, 0, -17.5 );
  Collision[0][2] := vec3f( -8.23, 0, 4.9 );

  Collision[1][0] := vec3f( -8.23, 0, 4.9 );
  Collision[1][1] := vec3f( 8.23, 0, 4.9 );
  Collision[1][2] := vec3f( 8.5, 0, -17.5 );

//  Health := random( 5 ) + 5;
  Health := random( 10 ) + 10;
  Speed := 250;
  Damage := 7 + random( 6 );
  color := vec3f( 0.1, 0.1, 0.75 );
end;

procedure TEnemy3.Update;
var
d, r : tvec3f;
i : Integer;
begin
  if Health <= 0 then
  begin
    inc( player.Score, reward );
    Boom;
    TWreckage.Create(matrix,enemy3,color);
    free;
    exit;
  end;

  TacticTicker := TacticTicker + corex.Render.DeltaTime;

  case Tactic of
  0 : ;
  1 : if TacticTicker >= 0.75 then
      begin
        TacticTicker := 0;
        TBomb.Create(dleft,0,true).Matrix := matrix;
      end;

  end;

  with matrix, pos do
  begin
  if homing then
    begin
      d := (pos - player.Matrix.Pos).normal;
      r := d.Cross(vec3f(0,1,0)).Normal;
      e02 := d.x;
      e12 := d.y;
      e22 := d.z;

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;
  end;

    Pos := pos + vec3f( e02, e12, e22 ) * -speed * corex.Render.DeltaTime;

    if ( z < -225 ) or ( Matrix.Pos.z > 225 )
    or ( x < -300 ) or ( Matrix.Pos.x > 300 ) then Free;

    for I := 0 to system.length( collision ) - 1 do
      if TriIntersect( matrix, collision[i][0],collision[i][1],collision[i][2],player.matrix.pos) then
      begin
      if player.Invincibility < 0 then
        begin
          dec( player.Health, 12 + random( 6 ) - 3 );
          dgsoundengine.Sounds['plr_hit'].play();
          (player).Color := vec3f( 1, 1, 1 );
          player.Invincibility := player.InvMax;
        end;
        health := -1;
        break;

      end;

//     begin color := vec3f( 1, 1, 1 ) end;

//    if pos.Dist(gc.Get(0).Matrix.Pos) < 10 then Free;
  end;

  Spin := Spin + 120 * deg2rad * corex.Render.DeltaTime;

  enemy3.matrix := matrix;
  enemy3.matrix.Rotate( -Spin, vec3f( 0, 0, 1 ) );

  color := color.Lerp( vec3f( 0.1, 0.1, 0.75 ), 0.95 * corex.render.deltatime );

  render;
end;

{ TBomb }

procedure TBomb.Render;
begin
  gl.Color3fv( color );
  bomb.Draw;
end;

procedure TBomb.Setup;
begin
  Reward := 5;

  SetLength( Collision, 8 );
  Collision[0][0] := vec3f( 2.5, 0, 6.1 );
  Collision[0][1] := vec3f( 6.1, 0, 2.5 );
  Collision[0][2] := vec3f( 0, 0, 0 );

  Collision[1][0] := vec3f( 6.1, 0, 2.5 );
  Collision[1][1] := vec3f( 6.1, 0, -2.5 );
  Collision[1][2] := vec3f( 0, 0, 0 );

  Collision[2][0] := vec3f( 6.1, 0, -2.5 );
  Collision[2][1] := vec3f( 2.5, 0, -6.1 );
  Collision[2][2] := vec3f( 0, 0, 0 );

  Collision[3][0] := vec3f( 2.5, 0, -6.1 );
  Collision[3][1] := vec3f( -2.5, 0, -6.1 );
  Collision[3][2] := vec3f( 0, 0, 0 );

  Collision[4][0] := vec3f( -2.5, 0, -6.1 );
  Collision[4][1] := vec3f( -6.1, 0, -2.5 );
  Collision[4][2] := vec3f( 0, 0, 0 );

  Collision[5][0] := vec3f( -6.1, 0, -2.5 );
  Collision[5][1] := vec3f( -6.1, 0, 2.5 );
  Collision[5][2] := vec3f( 0, 0, 0 );

  Collision[6][0] := vec3f( -6.1, 0, 2.5 );
  Collision[6][1] := vec3f( -2.5, 0, 6.1 );
  Collision[6][2] := vec3f( 0, 0, 0 );

  Collision[7][0] := vec3f( -2.5, 0, 6.1 );
  Collision[7][1] := vec3f( 2.5, 0, 6.1 );
  Collision[7][2] := vec3f( 0, 0, 0 );


  Health := random( 4 ) + 4;
  Speed := 150;
  Damage := 17 + random( 6 );
  color := vec3f( 0.6, 0.15, 0.35 );
end;

procedure TBomb.Update;
var
d, r : tvec3f;
begin
  if Health <=0 then
  begin
    inc( player.Score, reward );
    Boom;
    TWreckage.Create(matrix,bomb,color);
    free;
    exit;
  end;


  with matrix, pos do
  begin
  if homing then
    begin
      d := (pos - player.Matrix.Pos).normal;
      r := d.Cross(vec3f(0,1,0)).Normal;
      e02 := d.x;
      e12 := d.y;
      e22 := d.z;

      e00 := r.x;
      e10 := r.y;
      e20 := r.z;

      Homing := false;
  end;

    Pos := pos + vec3f( e02, e12, e22 ) * -speed * corex.Render.DeltaTime;

    if ( z < -225 ) or ( Matrix.Pos.z > 225 )
    or ( x < -300 ) or ( Matrix.Pos.x > 300 ) then Free;

    if pos.Dist(player.Matrix.Pos) < 10 then
    begin
      if player.Invincibility < 0 then
        begin
          dec( player.Health, Damage );
          dgsoundengine.Sounds['plr_hit'].play();
          (player).Color := vec3f( 1, 1, 1 );
          player.Invincibility := player.InvMax;
        end;
        health := -1;
    end;
  end;

  Spin := Spin + 360 * deg2rad * corex.Render.DeltaTime;

  bomb.matrix := matrix;
  bomb.matrix.Rotate( -Spin, vec3f( 0, 1, 0 ) );
  color := color.Lerp( vec3f( 0.6, 0.15, 0.35 ), 0.95 * corex.render.deltatime );

  render;
end;

{ TBossPart }

Constructor TBossPart.Create(fMaster: tboss; fOffset : tvec3f );
begin
inherited Create;
offset := fOffset;
Master := fmaster;
setup;
end;

procedure TBossPart.Render;
begin
  gl.Color3fv( color );
  Model.Draw;
end;

{procedure TBossPart.Setup;
begin
  Health := 500;
end;}

procedure TBossPart.Update;
begin
  if Health <=0 then
  begin
    inc( player.Score, reward );
    Boom;
    TWreckage.Create(matrix,model,color);
    Visible := false;
  end;

Matrix := master.Matrix;
with matrix, pos do
pos := pos + vec3f( e00, e10, e20 ) * offset.x + vec3f( e02, e12, e22 ) * offset.z;

color := Color.Lerp( vec3f( 0.25, 0.25, 0.25 ), 0.95 * corex.Render.DeltaTime );

Model.matrix := Matrix;
render;
end;

{ TBoss }

procedure TBoss.Free;
begin
  TWreckage.Create(matrix,bossbody,color);
  boss := nil;
  inherited;
end;

procedure TBoss.Render;
begin
  gl.Color3fv( Color );
  bossBody.Draw;
end;

procedure TBoss.Setup;
var
  I: Integer;
begin
  Reward := 1000;

  dgSoundEngine.Sounds['E-Cone - Theme1'].Stop;
  dgSoundEngine.Sounds['E-Cone - Theme2'].Play;

  Color := vec3f( 0.4, 0.05, 0.1 );
  matrix.Pos := vec3f( 0, 0, -199 );
  Health := 2800;

{$region 'collision'}
  SetLength( Collision, 30 );
  SetCol( 0, vec3f( 18, 0, 60), vec3f( 51, 0, 47 ), vec3f( 18, 0, 40 ) );
  SetCol( 1, vec3f( 51, 0, 20), vec3f( 51, 0, 47 ), vec3f( 18, 0, 40 ) );
  SetCol( 2, vec3f( 0, 0, 40), vec3f( 11, 0, -42 ), vec3f( 0, 0, 30 ) );
  SetCol( 3, vec3f( 0, 0, 40), vec3f( 11, 0, -42 ), vec3f( 18, 0, 40 ) );
  SetCol( 4, vec3f( 18, 0, 40), vec3f( 11, 0, -42 ), vec3f( 51, 0, 20 ) );
  SetCol( 5, vec3f( 51, 0, 20), vec3f( 11, 0, -42 ), vec3f( 30, 0, -43 ) );
  SetCol( 6, vec3f( 11, 0, -42), vec3f( 12, 0, -64 ), vec3f( 30, 0, -43 ) );
  SetCol( 7, vec3f( 12, 0, -64), vec3f( 25, 0, -64 ), vec3f( 30, 0, -43 ) );
  SetCol( 8, vec3f( 30, 0, -43), vec3f( 51, 0, 20 ), vec3f( 57, 0, 0 ) );
  SetCol( 9, vec3f( 30, 0, -43), vec3f( 57, 0, 0 ), vec3f( 63, 0, 0 ) );
  SetCol( 10, vec3f( 30, 0, -43), vec3f( 104, 0, 30 ), vec3f( 99, 0, -23 ) );
  SetCol( 11, vec3f( 30, 0, -43), vec3f( 63, 0, 0 ), vec3f( 104, 0, 30 ) );
  SetCol( 12, vec3f( 70, 0, 20), vec3f( 63, 0, 0 ), vec3f( 104, 0, 30 ) );
  SetCol( 13, vec3f( 70, 0, 20), vec3f( 70, 0, 40 ), vec3f( 104, 0, 30 ) );
  SetCol( 14, vec3f( 99, 0, -23), vec3f( 104, 0, 0 ), vec3f( 104, 0, 30 ) );

  SetCol( 15, vec3f( -18, 0, 60), vec3f( -51, 0, 47 ), vec3f( -18, 0, 40 ) );
  SetCol( 16, vec3f( -51, 0, 20), vec3f( -51, 0, 47 ), vec3f( -18, 0, 40 ) );
  SetCol( 17, vec3f( -0, 0, 40), vec3f( -11, 0, -42 ), vec3f( -0, 0, 30 ) );
  SetCol( 18, vec3f( -0, 0, 40), vec3f( -11, 0, -42 ), vec3f( -18, 0, 40 ) );
  SetCol( 19, vec3f( -18, 0, 40), vec3f( -11, 0, -42 ), vec3f( -51, 0, 20 ) );
  SetCol( 20, vec3f( -51, 0, 20), vec3f( -11, 0, -42 ), vec3f( -30, 0, -43 ) );
  SetCol( 21, vec3f( -11, 0, -42), vec3f( -12, 0, -64 ), vec3f( -30, 0, -43 ) );
  SetCol( 22, vec3f( -12, 0, -64), vec3f( -25, 0, -64 ), vec3f( -30, 0, -43 ) );
  SetCol( 23, vec3f( -30, 0, -43), vec3f( -51, 0, 20 ), vec3f( -57, 0, 0 ) );
  SetCol( 24, vec3f( -30, 0, -43), vec3f( -57, 0, 0 ), vec3f( -63, 0, 0 ) );
  SetCol( 25, vec3f( -30, 0, -43), vec3f( -104, 0, 30 ), vec3f( -99, 0, -23 ) );
  SetCol( 26, vec3f( -30, 0, -43), vec3f( -63, 0, 0 ), vec3f( -104, 0, 30 ) );
  SetCol( 27, vec3f( -70, 0, 20), vec3f( -63, 0, 0 ), vec3f( -104, 0, 30 ) );
  SetCol( 28, vec3f( -70, 0, 20), vec3f( -70, 0, 40 ), vec3f( -104, 0, 30 ) );
  SetCol( 29, vec3f( -99, 0, -23), vec3f( -104, 0, 0 ), vec3f( -104, 0, 30 ) );
{$endregion}

  setlength( Parts, 5 );
  parts[0] := TBossRotor.Create( self, vec3f( 60, 0, 0 ) );
  parts[1] := TBossRotor.Create( self, vec3f( -60, 0, 0 ) );
  parts[2] := TBossCoverL.Create( self, vec3f( 0, 0, 0 ) );
  parts[3] := TBossCoverR.Create( self, vec3f( 0, 0, 0 ) );
  parts[4] := TBossHead.Create( self, vec3f( 0, 0, 0 ) );

  SetLength( Guns, 18);

  Guns[0] := TGatlingTurret.Create( 10, self, vec3f( 35, 0, 30 ), false );
  Guns[1] := TGatlingTurret.Create( 10, self, vec3f( -35, 0, 30 ), false );

  Guns[2] := TGatlingTurret.Create( 15, self, vec3f( 87, 0, 12.5 ), false );
  Guns[2].RocketGun := true;
  Guns[2].gunDelay := 0.6;
  Guns[3] := TGatlingTurret.Create( 15, self, vec3f( -87, 0, 12.5 ), false );
  Guns[3].RocketGun := true;
  Guns[3].gunDelay := 0.6;

  Guns[4] := TGatlingTurret.Create( 10, parts[3], vec3f( 18.5, 0, 75 ), true ); //cover
  Guns[4].FixedAngle := pi;
  Guns[5] := TGatlingTurret.Create( 10, parts[2], vec3f( -18.5, 0, 75 ), true );
  Guns[5].FixedAngle := pi;

  Guns[6] := TDoubleTurret.Create( 10, parts[3], vec3f( 51.5, 0, 58.5 ), true );
  Guns[6].FixedAngle := pi;
  Guns[7] := TDoubleTurret.Create( 10, parts[2], vec3f( -51.5, 0, 58.5 ), true );
  Guns[7].FixedAngle := pi;

  Guns[8] := TDoubleTurret.Create( 20, parts[3], vec3f( 81, 0, 51 ), true );
  Guns[8].RocketGun := true;
  Guns[8].FixedAngle := pi;
//  Guns[8].gunDelay := 0.6;
  Guns[9] := TDoubleTurret.Create( 20, parts[2], vec3f( -81, 0, 51 ), true );
  Guns[9].RocketGun := true;
  Guns[9].FixedAngle := pi;
//  Guns[9].gunDelay := 0.6;

  Guns[10] := TDoubleTurret.Create( 15, self, vec3f( 10, 0, 20 ), true );
  Guns[10].FixedAngle := pi;
  Guns[11] := TDoubleTurret.Create( 15, self, vec3f( -10, 0, 20 ), true );
  Guns[11].FixedAngle := pi;

  Guns[12] := TGatlingTurret.Create( 15, self, vec3f( 20, 0, -60 ), true );//frontal
  Guns[13] := TGatlingTurret.Create( 15, self, vec3f( -20, 0, -60 ), true );

  Guns[14] := TGatlingTurret.Create( 10, self, vec3f( 50, 0, -30 ), true );//frontal
  Guns[15] := TGatlingTurret.Create( 10, self, vec3f( -50, 0, -30 ), true );

  Guns[16] := TGatlingTurret.Create( 10, self, vec3f( 80, 0, -20 ), true );//frontal
  Guns[17] := TGatlingTurret.Create( 10, self, vec3f( -80, 0, -20 ), true );


  for I := 0 to length( Guns ) - 3 do
  begin
    if Guns[i] is TGatlingTurret then
    with guns[i] as TGatlingTurret do
    begin
      gunClipSize := 8;
      gunClipDelay := 7;
      gunDelay := 0.75;

    end;
    Friendly := false;
  end;

end;

procedure TBoss.Update;
var
  I: Integer;
begin
  spMax := 0.75;
  Health := 0;
  for I := 0 to 5 - 1 do
    health := Health + parts[i].Health;
    if Health <= 0 then
    begin
    inc( player.Score, reward );
    Boom;
    for I := 0 to length(guns) - 1 do
      guns[i].Free;
    for I := 0 to length( Parts ) - 1 do
      parts[i].Free;
      
    dgSoundEngine.Sounds['E-Cone - Theme2'].Stop;    
    dgSoundEngine.Sounds['E-Cone - Theme1'].play;
      free;
      exit;
    end;


with matrix do
begin

  if player.Matrix.Pos.Dist( Pos ) <= 110 then player.Matrix.Pos := Pos + ( Pos - player.Matrix.Pos ).Normal * -110;

  case Tactic of
  0 :
    begin
      pos := pos + vec3f( 0, 0, 50 * corex.Render.DeltaTime );
      if pos.z >= 50 then tactic := 1;

    end;
  1 :
    begin
      if MoveLeft then
        Speed := lerp( speed, -50, 0.95 * corex.render.DeltaTime )
      else
        Speed := lerp( speed, 50, 0.95 * corex.render.DeltaTime );
      if pos.x < -100 then MoveLeft := false;
      if pos.x > 100 then MoveLeft := true;
      Pos := vec3f( pos.x + speed * corex.Render.DeltaTime, 0, lerp( pos.z, -100 + abs(pos.x) / 2, 0.95 * corex.Render.DeltaTime) );

      TacticTicker := TacticTicker + corex.Render.DeltaTime;
      if TacticTicker > 5 then
      begin
        TacticTicker := 0;
        Tactic := random(2);

      end;
    end;
  end;

  for I := 0 to 4 - 1 do
    begin
      guns[i].Enabled := ((parts[2].Health<=0) and (parts[3].Health<=0)) or ( ( parts[0].Health<=0 ) or ( parts[1].Health<=0 ) );

    end;

end;

  color := {color.lerp}( vec3f( 0.4, 0.05, 0.1 ){, 1.99 * corex.Render.DeltaTime} );
  if (parts[0].Health <=0) or (parts[1].Health<=0) then matrix.Rotate( pi/4 * corex.render.deltatime, vec3f( 0, 1, 0 ));
  bossBody.matrix := matrix;

  render;
end;

{ TBossRotor }

procedure TBossRotor.Render;
begin
  gl.Color3fv( color );
  model.Draw;
end;

procedure TBossRotor.Setup;
begin
  Reward := 50;
  health := 250;
  setlength( collision, 1 );
  SetCol( 0, vec3f( 0, 0, 0 ), vec3f( 7.5, 0, 46), vec3f( -7.5, 0, 46) );
  model := bossRotor;
end;

procedure TBossRotor.Update;
begin
if Health <=0 then
begin
    inc( player.Score, reward );
Boom;
    TWreckage.Create(matrix,bossrotor,color);
Visible := false;
end;

  spin := spin + 1720 * corex.Render.DeltaTime;
  Matrix := master.Matrix;
  with matrix, pos do
    pos := pos + vec3f( e00, e10, e20 ) * offset.x + vec3f( e02, e12, e22 ) * offset.z;

  color := color.Lerp( vec3f( 0.25, 0.25, 0.25 ), 0.95 * corex.Render.DeltaTime);

  Model.matrix := Matrix;
  model.matrix.Rotate( spin * deg2rad, vec3f(0,0,1));
  render;
end;

{ TBossCoverL }

procedure TBossCoverL.Render;
begin
  inherited;

end;

procedure TBossCoverL.Setup;
begin
  Reward := 110;
  model := bossCoverL;
  Health := 1000;
  SetLength( Collision, 4 );
  SetCol( 0, vec3f( 0, 0, 63 ), vec3f( -19, 0, 80 ), vec3f( -6, 0, 91 ) );
  SetCol( 1, vec3f( 0, 0, 63 ), vec3f( -19, 0, 80 ), vec3f( -16, 0, 63 ) );
  SetCol( 2, vec3f( -19, 0, 80 ), vec3f( -101, 0, 52 ), vec3f( -104, 0, 32 ) );
  SetCol( 3, vec3f( -19, 0, 80 ), vec3f( -16, 0, 63 ), vec3f( -104, 0, 52 ) );
end;

procedure TBossCoverL.Update;
begin
  inherited;

end;

{ TBossCoverR }

procedure TBossCoverR.Render;
begin
  inherited;

end;

procedure TBossCoverR.Setup;
begin
  Reward := 110;
  model := bossCoverR;
  health := 1000;
  SetLength( Collision, 4 );
  SetCol( 0, vec3f( 0, 0, 63 ), vec3f( 19, 0, 80 ), vec3f( 6, 0, 91 ) );
  SetCol( 1, vec3f( 0, 0, 63 ), vec3f( 19, 0, 80 ), vec3f( 16, 0, 63 ) );
  SetCol( 2, vec3f( 19, 0, 80 ), vec3f( 101, 0, 52 ), vec3f( 104, 0, 32 ) );
  SetCol( 3, vec3f( 19, 0, 80 ), vec3f( 16, 0, 63 ), vec3f( 104, 0, 52 ) );
end;

procedure TBossCoverR.Update;
begin
  inherited;

end;

{ TBossHead }

procedure TBossHead.Render;
begin
  inherited;

end;

procedure TBossHead.Setup;
begin
  Reward := 250;
  model := bossHead;
  Health := 1000;
  SetLength( Collision, 3 );
  SetCol( 0, vec3f( 0, 0, -84 ), vec3f( 11, 0, -81 ), vec3f( 11, 0, -44 ) );
  SetCol( 1, vec3f( 0, 0, -84 ), vec3f( -11, 0, -81 ), vec3f( -11, 0, -44 ) );
  SetCol( 2, vec3f( 0, 0, -84 ), vec3f( -11, 0, -44 ), vec3f( 11, 0, -44 ) );

end;

procedure TBossHead.Update;
begin
  inherited;

end;

{ TTurret }

procedure TTurret.Update;
begin
  if master = nil then
  free
  else
  Visible := master.Visible;
  
end;

{ TEnemySpawner }

function TEnemySpawner.CurrEvent: TSubEvent;
begin
  result := events[currentevent];
end;

procedure TEnemySpawner.Free;
var
  I: Integer;
begin
  for I := 0 to length( Events ) - 1 do
    events[i].Destroy;
  inherited;
end;

function TEnemySpawner.LastEvent: TSubEvent;
begin
  result := Events[ length( events ) - 1 ];
end;

function TEnemySpawner.LastSpawner: TSubSpawner;
begin
  result := LastEvent.LastSubSpawner;
end;

procedure TEnemySpawner.Load(fFileName: String);
var
f, c : tstringlist;
  I: Integer;
  tmp : string;
begin
f := TStringList.Create;
f.LoadFromFile( fFileName );
c := TStringList.Create;

for I := 0 to f.Count - 1 do
begin
  c.CommaText := f[i];
  if c.Count > 0 then
  begin
    tmp := c[0];
    if c[0] = 'begin' then TSubEvent.Add;
    if c[0] = 'time>' then es.LastEvent.TimeCondition := conv( c[1], 0.0 );
    if c[0] = 'foes<=' then es.LastEvent.EnemyCondition := conv( c[1], 0 );
    if c[0] = 'timeoffset>' then es.LastEvent.TimeOffset := conv( c[1], 0.0 );
    if c[0] = 'credits' then es.LastEvent.Credits := true;


    if (c[0][1] = 'e') and (c[0] <> 'end') then
      begin
      TSubSpawner.Create( es.LastEvent );
      with es.LastSpawner do
      begin
        TimeOffsetCondition := es.LastEvent.TimeOffset;
        EnemyType := conv( copy( c[0], 2, length( c[0] ) - 1 ), 0 );

        if c[1] = 'top' then Appearance := dTop
        else
        if c[1] = 'left' then Appearance := dLeft
        else
        if c[1] = 'right' then Appearance := dRight
        else
        Appearance := dBottom;

        ScreenFactor := conv( c[2], 0 );

        Homing := conv( c[3], false );

        Tactic := conv( c[4], 0 );
      end;

      end;
  end;
  
  

end;

f.Free;
c.Free;
ES.CurrentEvent := 0;
end;

procedure TEnemySpawner.Render;
begin
//nothing
end;

procedure TEnemySpawner.Update;
var
  I: Integer;
begin
  Time := time + corex.Render.DeltaTime;
  if CurrentEvent < length( Events ) then
  begin
    if Time > CurrEvent.TimeCondition then
    if gc.Enemies <= CurrEvent.EnemyCondition then
    CurrEvent.Started := true;

    if CurrEvent.Started then
    begin
      if currevent.credits then
      begin
        credits := true;
      end;

      CurrEvent.TimeOffset := CurrEvent.TimeOffset + corex.Render.DeltaTime ;

      for I := 0 to length( CurrEvent.Spawners ) - 1 do
      CurrEvent.spawners[i].execute;

      if CurrEvent.Completed then
      begin
        time := 0;
        inc( CurrentEvent );
        if CurrentEvent < length( Events ) then CurrEvent.TimeOffset := 0;
      end;
    end;

  end;

end;

{ TSpawner }

Constructor TSubSpawner.Add( fTOC : real; fET : byte; fA : TAppearance; fSF : byte; fH : boolean; fT : Byte; fsubevent : TSubEvent);
begin
inherited;
  Completed := false;
  with fSubEvent do
  begin
  TimeOffsetCondition := fTOC;
  EnemyType := fET;
  Appearance := fA;
  ScreenFactor := fSF;
  Homing := fH;
  Tactic := fT;

    setlength( Spawners, length( Spawners ) + 1 );
    Spawners[length(Spawners)-1] := self;
  end;
end;

{ TSubEvent }

Constructor TSubEvent.Add;
begin
inherited;
  with ES do
  begin
    setlength( Events, length( Events ) + 1 );
    Events[length(Events)-1] := self;
  end;

end;

constructor TSubSpawner.Create(fsubevent: TSubEvent);
begin
  with fsubevent do
  begin
    setlength( Spawners, length( Spawners ) + 1 );
    Spawners[length(Spawners)-1] := self;
  end;
end;

procedure TSubSpawner.Execute;
begin
if not Completed then
if es.CurrEvent.TimeOffset > es.CurrEvent.TimeCondition + TimeOffsetCondition then

begin
  case EnemyType of
  1 : tenemy1.Create( Appearance, ScreenFactor, Homing ).Tactic := Tactic;
  2 : tenemy2.Create( Appearance, ScreenFactor, Homing ).Tactic := Tactic;
  3 : tenemy3.Create( Appearance, ScreenFactor, Homing ).Tactic := Tactic;
  4 : tbomb.Create( Appearance, ScreenFactor, Homing ).Tactic := Tactic;
  255 : boss := tboss.Create( Appearance, ScreenFactor, Homing );
  end;
  Completed := true;
end;

end;

function TSubEvent.Completed: Boolean;
var
  I: Integer;
begin
result := true;
for I := 0 to length( Spawners ) - 1 do
result := result and Spawners[i].Completed;
end;

function TSubEvent.LastSubSpawner: TSubSpawner;
begin
  result := Spawners[ length( Spawners ) - 1 ];
end;

{ TWreckage }

constructor TWreckage.Create(fMat : TMat4f; fModel: tMeshContainer; fColor : TVec3f );
begin
inherited Create;
  Color := fColor;
  Visible := true;
  Matrix := fMat;
  Model := fModel;
end;

procedure TWreckage.Render;
begin
   gl.Color3fv( Color );
   Model.Draw;
end;

procedure TWreckage.Update;
begin
  with matrix, pos do
  begin
    if y <= -300 then begin free; exit; end;

    spinY := spinY + 180 * corex.Render.DeltaTime;

    color := color.Lerp( vec3f( 0.25, 0.25, 0.25 ), 2 *  corex.Render.DeltaTime );

    pos := pos - vec3f( 0, 100 * corex.render.deltatime, - 300 * corex.Render.DeltaTime );
  end;
  Model.matrix := Matrix;
  Model.Matrix.Rotate( spinY * Deg2Rad, vec3f ( 0, 1, 0 ));

  render;
end;

initialization
GC.Count := 0;
es := TEnemySpawner.Create;
es.Time := 0;

finalization
GC.Kill;

end.
