unit l_zenit;

interface

uses
 OpenGL,
 l_gl, l_math, l_model_v2, l_sound, l_spider;

type
 PZenit = ^TZenit;
 
 TZenit = object
   tts    : PSpider;
   tAngle : TVector;
   target : boolean;    // есть ли цель
   Pos    : TVector;
   Angle  : TVector;
   m      : TMatrix4;

   Reload   : integer;
   DieTimer : integer;
   Next   : PZenit;
   Prev   : PZenit;
   procedure Init;
   procedure GetAngle(tPos: TVector);
   procedure AI;
   procedure Die;
   procedure ReCalc;
   procedure Update;
   procedure Render;
 end;

const
 ZENIT_RELOAD = 10;
 ZENIT_DIFF   = 16;

var
 snd__zenit_fire : integer;
 snd__zenit_aim  : integer;
 
implementation

uses
 l_camera, l_game;
 
procedure TZenit.Init;
begin
Angle  := Vector(0, 0, 0);
tAngle := Vector(0, 0, 0);
Reload   := 0;
DieTimer := 150;
target := false;
Prev := nil;
Next := nil;
tts  := nil;
end;

procedure TZenit.GetAngle(tPos: TVector);
var
 p : TVector;
begin
// Угол отсносительно Y
p.X := tPos.X - Pos.X;
p.Y := 0;
p.Z := tPos.Z - Pos.Z;
tAngle.Y := V_Angle(Vector(1, 0, 0), p);
if p.Z < 0 then tAngle.Y := 2 * pi - tAngle.Y;
if (Angle.Y - tAngle.Y) > pi then tAngle.Y := tAngle.Y + pi * 2;
if (tAngle.Y - Angle.Y) > pi then tAngle.Y := tAngle.Y - pi * 2;

// Угол относительно X
p.Y := tPos.Y - Pos.Y;
tAngle.X := V_Angle(Vector(p.X, 0, p.Z), p);
if p.Y < 0 then tAngle.X := 2 * pi - tAngle.X;
if (Angle.X - tAngle.X) > pi then tAngle.X := tAngle.X + pi * 2;
if (tAngle.X - Angle.X) > pi then tAngle.X := tAngle.X - pi * 2;
end;

procedure TZenit.AI;
var
 s, ts   : PSpider;
 dist, r : single;
begin
s    := Map.Spider;
ts   := nil;
dist := 4096;  // Видимость 64 м.
while s <> nil do
 begin
 r := V_Distq(s^.Pos, Pos);
 if dist > r then
  begin
  dist := r;
  ts   := s;
  end;
 s := s^.Next; 
 end;

if (ts <> tts) then
 snd_Play(snd__zenit_aim, false, Pos);
target := ts <> nil;
tts := ts;
if target then GetAngle(ts^.Pos);
end;

procedure TZenit.Die;
begin
tAngle   := Vector(pi/2, Angle.Y, 0);
DieTimer := 149;
end;

procedure TZenit.ReCalc;
var
 Dir, Left, Up : TVector;
begin
// Расчёт матрицы поворота для стойки
Map.Collision(Pos, Up);
// Находим 3 вектора базиса
Dir  := Vector(1, 0, 0);
Left := V_Cross(Dir, Up);
Dir  := V_Cross(Left, Up);
Dir  := V_Normalize(Dir);
Left := V_Normalize(Left);
// Заполняем матрицу
m[0][0] := Dir.X;  m[0][1] := Dir.Y;  m[0][2] := Dir.Z;  m[0][3] := 0;
m[1][0] := Up.X;   m[1][1] := Up.Y;   m[1][2] := Up.Z;   m[1][3] := 0;
m[2][0] := Left.X; m[2][1] := Left.Y; m[2][2] := Left.Z; m[2][3] := 0;
m[3][0] := Pos.X;  m[3][1] := Pos.Y;  m[3][2] := Pos.Z;  m[3][3] := 1;
end;

procedure TZenit.Update;
const
 GUN_LEN = 1.7;
var
 p : TVector;
begin
AI;
// интерполяция углов поворота
Angle.X := Angle.X + (tAngle.X - Angle.X)/ZENIT_DIFF;
Angle.Y := Angle.Y + (tAngle.Y - Angle.Y)/ZENIT_DIFF;

if DieTimer = 150 then
 begin
 if Reload > 0 then
  dec(Reload);
 if target and (Reload = 0) then
  begin
  Reload := ZENIT_RELOAD;
  p.X := cos(Angle.Y) * cos(Angle.X);
  p.Y := sin(Angle.X);
  p.Z := sin(Angle.Y) * cos(Angle.X);
  p := V_Mult(p, GUN_LEN);
  p.Y := p.Y + 1.7;
  p := V_Add(Pos, p);
  snd_Play(snd__zenit_fire, false, p);
  Map.Fire(p, Angle);
  end;
 end
else
 dec(DieTimer);
end;

procedure TZenit.Render;
var
 i : integer;
begin
if (not zenit_xmd.Loaded) or (zenit_xmd.Meshes <> 3) then Exit;
if culling and not Camera.inFrustum(Pos, 3) then
 Exit;

glBlend(1);
glColor4f(0.4, 0.4, 0.4, DieTimer/150);
glPushMatrix;
glPushMatrix;
glMultMatrixf(@m);

with zenit_xmd do
 for i := 0 to 2 do
  begin
   case i of
   1 : begin
       glPopMatrix;
       glTranslatef(Pos.X, Pos.Y, Pos.Z);
       glRotatef(180 - Angle.Y * rad2deg, 0, 1, 0);
       end;
   2 : begin
       glTranslatef(0, 1.7, 0);
       glRotatef(-Angle.X * rad2deg, 0, 0, 1);
       end;
   end;
  with Mesh[i] do
   begin
   glNormalPointer(GL_FLOAT, 0, @Normal[0]);
   glVertexPointer(3, GL_FLOAT, 0, @Vertex[0]);
   glDrawElements(GL_TRIANGLES, F_Count*3, GL_UNSIGNED_SHORT, @Face[0]);
   end;
  end;
glPopMatrix;  
end;

end.
