unit SnakeUnits;

interface
  uses  windows, RoadGlobal, RoadSys, RoadUnit, RoadPhysic, RoadGraphic, opengl, math;

type
  TSnakePhysic  = class(TPhysicBase)
    Angle : Single;
    Speed : Single;
    Body  : array of TPosition;
    BodyCount : integer;
    FireDelay : integer;
    BomB      : Boolean;
    constructor Create(pParent : pointer);override;
    procedure StepLeft;
    procedure StepRight;
    procedure StepDown;
    procedure StepUp;
    procedure Step;
    procedure StepBody;
    procedure Update;override;
    procedure DetonateBomb;
    procedure FireTo(X,Z : Single);
  end;

  TSnakeGraphic = class(TGraphicBase)
    procedure Draw;override;
  end;


  TEnemyPhysic  = class(TPhysicBase)
    Angle : Single;
    Speed : Single;
    constructor Create(pParent : pointer);override;
    procedure Update;override;
  end;

  TEnemyGraphic  = class(TGraphicBase)
    procedure Draw;override;
  end;

  TBulletPhysic = class(TPhysicBase)
    Angle : Single;
    speed : Single;
    procedure update;override;
  end;

  TBulletGraphic  = class(TGraphicBase)
    procedure Draw;override;
  end;

  TFoodPhysic     = class(TPhysicBase)
    constructor Create(pParent : pointer);override;
    procedure update;override;
    destructor Destroy;override;
  end;

  TFoodGraphic  = class(TGraphicBase)
    procedure Draw;override;
  end;

  TBombPhysic     = class(TPhysicBase)
    Timer : dword;
    radius : Single;
    constructor Create(pParent : pointer);override;
    procedure update;override;
    destructor Destroy;override;
  end;

  TBombGraphic  = class(TGraphicBase)
    procedure Draw;override;
  end;
{  TSnake  = class(TRoadUnit)

  end;

  PSnake  = ^TSnake;}
implementation
  uses
    Scenes;
{ TSnakePhysic }

constructor TSnakePhysic.Create;
begin
  inherited;
  Speed :=  10000.1;
end;

procedure TSnakePhysic.DetonateBomb;
var
  i : integer;
begin
  Bomb  :=  False;
  i :=  1;
  with (Scene as TSceneGame) do
  begin
    while (Units[i]<>nil) and (i<high(Units)) do inc(i);
    if i=high(Units) then
      begin
        SetLength(Units,UnitCount+1000);
        i := UnitCount;
      end;
        inc(UnitCount);
  end;
  (Scene as TSceneGame).Units[i]  :=  TRoadUnit.Construct(TBombPhysic,TBombGraphic);
  with (Scene as TSceneGame).Units[i] do
      Physic.Position :=  Position;
end;

procedure TSnakePhysic.FireTo(X, Z: Single);
var
  gip : single;
  ang : single;
  i   : integer;
begin
  i :=  0;
  if FireDelay>0 then Exit;
  FireDelay :=  round(1000-BodyCount*1000/1000);
  with (Scene as TSceneGame) do
  begin
    while (Units[i]<>nil) and (i<high(Units)) do inc(i);
    if i=high(Units) then
      begin
        SetLength(Units,UnitCount+1000);
        i := UnitCount;
      end;
        inc(UnitCount);
  end;
//  SetLength((Scene as TSceneGame).Units,length((Scene as TSceneGame).Units)+1);
  (Scene as TSceneGame).Units[i]  :=  TRoadUnit.Construct(TBulletPhysic,TBulletGraphic);
  with (Scene as TSceneGame).Units[i] do
    begin
      Physic.Position :=  Position;
      gip :=  sqrt(sqr(Physic.Position.X-X)+sqr(Physic.Position.Z-Z));
//      (Physic as TBulletPhysic).Angle  :=ArcTanh(((Physic.Position.X-X)/gip)/((Physic.Position.Z-Z)/gip));

      ang :=  ArcCos((Physic.Position.X-X)/gip);
      if (Physic.Position.Z-Z)<0 then ang := 0-ang;
      (Physic as TBulletPhysic).Angle  := ang;

    end;
end;

procedure TSnakePhysic.Step;
begin
  with Position do
   begin
    z :=  z - sin(Angle)*speed/dt/1000;
    x :=  x - cos(Angle)*speed/dt/1000;
   end;
  StepBody;
end;

procedure TSnakePhysic.StepBody;
var
  i : integer;
begin
  for i:=BodyCount-1 downto 1 do
    Body[i]:=Body[i-1];
  if BodyCount>0 then
    Body[0] :=  Position;
end;

procedure TSnakePhysic.StepDown;
begin
  with Position do
    z :=  z + speed/dt/1000;
  StepBody;
end;

procedure TSnakePhysic.StepLeft;
begin
  with Position do
    x :=  x - speed/dt/1000;
  StepBody;
end;

procedure TSnakePhysic.StepRight;
begin
  with Position do
    x :=  x + speed/dt/1000;
  StepBody;
end;

procedure TSnakePhysic.StepUp;
begin
  with Position do
    z :=  z - speed/dt/1000;
  StepBody;
end;

procedure TSnakePhysic.Update;
var
  Z : Single;
  mview : array[1..16] of Double;
  proj  : array[1..16] of Double;
  view  : array[1..4] of integer;
  bx,by,bz : Double;
  ry : integer;
begin
  if BodyCount<0 then BodyCount :=  0;
  FireDelay:=FireDelay-dt;
  if Application.Mouse.RButton then
    if BomB then
      DetonateBomb;
  if Application.Keys[ord('W')]=1 then
    Step;
  StepBody;
  if Position.X<-5 then
    Position.X := -5;
  if Position.X>5 then
    Position.X := 5;
  if Position.Z<-5 then
    Position.Z := -5;
  if Position.Z>5 then
    Position.Z := 5;
//  if Application.Keys[vk_down]=1 then
//    StepDown;

  if Application.Keys[ord('A')]=1 then
    Angle:=Angle-0.1;
  if Application.Keys[ord('D')]=1 then
    Angle:=Angle+0.1;
  if Application.Keys[ord(' ')]=1 then
    Position.Y  :=  Position.Y + speed/dt/1000
      else
        Position.Y  :=  Position.Y - speed/dt/1000;
  if Position.Y<0 then Position.Y :=  0;
  if Application.Mouse.LButton then
    begin
      Render.SetRenderContext;
      (Scene as TSceneGame).SelectMode  :=  True;
      Scene.DrawScene;
      (Scene as TSceneGame).SelectMode  :=  False;
      glBegin(GL_QUADS);
      glVertex3f(-30,0,-30);
      glVertex3f(-30,0,30);
      glVertex3f(30,0,30);
      glVertex3f(30,0,-30);
      glEnd;
      glGetDoublev(GL_MODELVIEW_MATRIX,@mview[1]);
      glGetDoublev(GL_PROJECTION_MATRIX,@proj[1]);
      glGetIntegerv(GL_VIEWPORT,@view[1]);
      ry := Application.Window.Height-Application.Mouse.Y;
      glReadPixels(Application.Mouse.X,ry,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,@Z);
      bx:=0;
      by:=0;
      bz:=0;
      gluUnProject(Application.Mouse.X,ry,z,@mview[1],@proj[1],
      @view[1],BX,BY,BZ);
      Render.FreeRenderContext;
      FireTo(BX,BZ);
    end;
end;

{ TSnakeGraphic }

procedure TSnakeGraphic.Draw;
var
  q : GLUquadricObj;
  P : TRoadUnit;
  i : integer;
begin
  inherited;
  P :=  Parent;
  q :=  gluNewQuadric;
  with (P.Physic as TSnakePhysic) do
    begin
      glPushMatrix;
      glColor3f(0,1,0);
      glTranslatef(Position.X,Position.Y,Position.Z);
      gluSphere(q,0.1,4,4);
      glPopMatrix;
      for i:=0 to BodyCount-1 do
        begin
          glPushMatrix;
          glTranslatef(Body[i].X,Body[i].Y,Body[i].Z);
          gluSphere(q,0.1,3,3);
          glPopMatrix;
        end;
    end;
  gluDeleteQuadric(q);
end;

{ TBulletPhysic }

procedure TBulletPhysic.update;
var
  i : integer;
begin
  inherited;
  speed:=20000;
  if sqrt(sqr(Position.X)+sqr(Position.Z))>=10 then
    begin
      TRoadUnit(Parent).Die;
      Exit;
    end;
  with Position do
   begin
    z :=  z - sin(Angle)*speed/dt/1000;
    x :=  x - cos(Angle)*speed/dt/1000;
    with (Scene as TSceneGame) do
      for i:=1 to UnitCount-1 do
        if Units[i]<>nil then
          if Units[i].UID<>TRoadUnit(Parent).UID then
            if TRoadUnit(Parent).isCollide(Units[i].Physic.Position) then
              begin
                Units[i].Die;
               (TRoadUnit(Parent)).Die;
                Exit;
              end;
   end;
end;

{ TBulletGraphic }

procedure TBulletGraphic.Draw;
var
  q : GLUquadricObj;
  P : TRoadUnit;
  i : integer;
begin
  inherited;
  P :=  Parent;
  q :=  gluNewQuadric;
  with (P.Physic) do
    begin
      glPushMatrix;
      glColor3f(1,0.5,0);
      glTranslatef(Position.X,Position.Y,Position.Z);
      gluSphere(q,0.1,4,4);
      glPopMatrix;
    end;
  gluDeleteQuadric(q);
end;

{ TEnemyPhysic }

constructor TEnemyPhysic.Create;
var
  r : integer;
begin
  inherited;
  r :=  random(100) div 25;
  case r of
    0:
      begin
        Position.X  :=  -5;
        Position.Z  :=  5-random(10);
      end;
    1:
      begin
        Position.X  :=  5;
        Position.Z  :=  5-random(10);
      end;
    2:
      begin
        Position.X  :=  5-random(10);
        Position.Z  :=  -5;
      end;
    3:
      begin
        Position.X  :=  5-random(10);
        Position.Z  :=  5;
      end;
  end;
end;

procedure TEnemyPhysic.Update;
var
  i : integer;
  gip : Single;
  mgip : Single;
  tx,tz : single;
  ang   : Single;
begin
  inherited;
  speed :=  1000;
  mgip:=100000;
  with ((Scene as TSceneGame).Units[0].Physic as TSnakePhysic) do
  begin
  for i:=0 to BodyCount-1 do
    begin
      if TRoadUnit(Self.Parent).isCollide(Body[i]) then
        begin
          Dec(((Scene as TSceneGame).Units[0].Physic as TSnakePhysic).BodyCount);
          TRoadUnit(Self.Parent).Die;
          Exit;
        end;
      gip :=  sqrt(sqr(Body[i].X-Self.Position.X)+sqr(Body[i].Z-Self.Position.Z));
      if gip<mgip then
        begin
          mgip  :=  gip;
          tx  :=  body[i].X;
          tz  :=  body[i].Z;
        end;
    end;
  end;

      if TRoadUnit(Self.Parent).isCollide(((Scene as TSceneGame).Units[0].Physic as TSnakePhysic).Position) then
        begin
          Dec(((Scene as TSceneGame).Units[0].Physic as TSnakePhysic).BodyCount);
          TRoadUnit(Self.Parent).Die;
          Exit;
        end;
      gip :=  sqrt(sqr((Scene as TSceneGame).Units[0].Physic.Position.X-Self.Position.X)+sqr((Scene as TSceneGame).Units[0].Physic.Position.Z-Self.Position.Z));
      if gip<mgip then
        begin
          mgip  :=  gip;
          tx  :=  (Scene as TSceneGame).Units[0].Physic.Position.X;
          tz  :=  (Scene as TSceneGame).Units[0].Physic.Position.Z;
        end;
//      (Physic as TBulletPhysic).Angle  :=ArcTanh(((Physic.Position.X-X)/gip)/((Physic.Position.Z-Z)/gip));

      ang :=  ArcCos((Position.X-tX)/mgip);
      if (Position.Z-tZ)<0 then ang := 0-ang;
      Angle :=  ang;
//      if Angle=-NAN then
//        Angle :=  0;
  with Position do
   begin
    z :=  z - sin(Angle)*speed/dt/1000;
    x :=  x - cos(Angle)*speed/dt/1000;
  if Position.X<-5 then
    Position.X := -5;
  if Position.X>5 then
    Position.X := 5;
  if Position.Z<-5 then
    Position.Z := -5;
  if Position.Z>5 then
    Position.Z := 5;
   end;
end;

{ TEnemyGraphic }

procedure TEnemyGraphic.Draw;
var
  q : GLUquadricObj;
  P : TRoadUnit;
  i : integer;
begin
  inherited;
  P :=  Parent;
  q :=  gluNewQuadric;
  with (P.Physic) do
    begin
      glPushMatrix;
      glColor3f(1,0.5,1);
      glTranslatef(Position.X,Position.Y,Position.Z);
      gluSphere(q,0.1,4,4);
      glPopMatrix;
    end;
  gluDeleteQuadric(q);
end;

{ TFoodPhysic }

constructor TFoodPhysic.Create(pParent: pointer);
begin
  inherited;
  Position.X :=  5-random(10);
  Position.Z  :=  5-random(10);
end;

destructor TFoodPhysic.Destroy;
var
  i : integer;
begin
  i :=  0;
  if (Scene as TSceneGame).Units[0]<>nil then
  begin
      with (Scene as TSceneGame) do
      begin
        while i<UnitCount do
         begin
          if Units[i]=nil then
              break;
          inc(i);
         end;
        if i=UnitCount then
          begin
            inc(UnitCount);
            i :=  UnitCount-1;
          end;
        Units[i]  :=  TRoadUnit.Construct(TFoodPhysic,TFoodGraphic);
        inc((Units[0].Physic as TSnakePhysic).BodyCount);
        SetLength((Units[0].Physic as TSnakePhysic).Body,(Units[0].Physic as TSnakePhysic).BodyCount);
      end;
  end;    
  inherited;
end;

procedure TFoodPhysic.update;
begin
  inherited;
  with (Scene as TSceneGame) do
  if TRoadUnit(Parent).isCollide(Units[0].Physic.Position) then
    begin
      TRoadUnit(Parent).Die;
      Exit;
    end;

end;

{ TFoodGraphic }


procedure TFoodGraphic.Draw;
var
  q : GLUquadricObj;
  P : TRoadUnit;
  i : integer;
begin
  inherited;
  P :=  Parent;
  q :=  gluNewQuadric;
  with (P.Physic) do
    begin
      glPushMatrix;
      glColor3f(1,1,1);
      glTranslatef(Position.X,Position.Y,Position.Z);
      gluSphere(q,0.1,4,4);
      glPopMatrix;
    end;
  gluDeleteQuadric(q);
end;

{ TBombPhysic }

constructor TBombPhysic.Create(pParent: pointer);
begin
  inherited;
  Timer :=  GetTickCount;
end;

destructor TBombPhysic.Destroy;
begin

  inherited;
end;

procedure TBombPhysic.update;
var
  i : integer;
begin
  inherited;
  Radius  :=  ((GetTickCount-Timer)/2000)*2;
  if Radius>=2 then
   begin
    TRoadUnit(Parent).Die;
    Exit;
   end;
  for i:=1 to (Scene as  TSceneGame).UnitCount-1 do
    if (Scene as  TSceneGame).Units[i]<>nil then
      if (Scene as  TSceneGame).Units[i].UID<>TRoadUnit(Parent).UID then
      with (Scene as  TSceneGame).Units[i] do
        if sqrt(
            sqr(Physic.Position.X-Position.X)
              +
                sqr(Physic.Position.Z-Position.Z)
                ) <Radius then
                  Die;
end;

{ TBombGraphic }

procedure TBombGraphic.Draw;
var
  q : GLUquadricObj;
  P : TRoadUnit;
  i : integer;
begin
  inherited;
  P :=  Parent;
  q :=  gluNewQuadric;
  with (P.Physic) do
    begin
      glPushMatrix;
      glColor3f(1,0,0);
      glTranslatef(Position.X,Position.Y,Position.Z);
      gluSphere(q,(TRoadUnit(Parent).Physic as TBombPhysic).radius,8,8);
      glPopMatrix;
    end;
  gluDeleteQuadric(q);
end;

end.
