unit UWorm;

interface

uses
  JEN_Header,
  JEN_Math;

const SEGMENT_SIZE = 18;
      SEGMENT_DIST = 8;
      EAT_DIST     = 13;

type TWorm = class
  constructor Create;
  private
    FTraceTex       : ITexture;
    FTex            : array[1..3] of ITexture;
    FSegmentsCount  : Word;
    FSpeed          : Single;
    FTrace          : array[1..500] of TVec2f;
    FPhase          : Single;
    FTraceDist      : Single;
    FBerserkStart   : LongInt;
    FLastEat        : LongInt;
    FSegments       : array[1..128] of record
      Pos : TVec2f;
      Dir : TVec2f;
    end;
  public
    function HeadPos : TVec2f;
    procedure OnUpdate(dt: LongInt);
    procedure OnRenderTrace;
    procedure OnRender;
end;

implementation

uses
  UGame,
  UMain;

constructor TWorm.Create;
var
  i : integer;
begin
  FSegmentsCount := 10;
  FBerserkStart := -1;
  FLastEat := Utils.Time;
  ResMan.Load('Media\Head.dds', FTex[1]);
  ResMan.Load('Media\Body.dds', FTex[2]);
  ResMan.Load('Media\Trace.dds', FTraceTex);

  FSegments[1].Dir := Vec2f(1,0);
  FSegments[1].Pos:= Vec2f(WorldSize, WorldSize)*0.5;

  for i := 1 to High(FTrace) do
    FTrace[i] := Vec2f(-10,-10);
end;

function TWorm.HeadPos : TVec2f;
begin
  Result := FSegments[1].Pos;
end;

procedure TWorm.OnUpdate(dt: LongInt);
var
  i   : integer;
  Dir : TVec2f;
  Rot : Single;
  Speed : Single;

  procedure AddSegments(Count : Integer);
  var
    j : integer;
  begin
    Inc(FSegmentsCount, Count);
    for j := FSegmentsCount downto (Count+1) do
    begin
      FSegments[j].Pos := FSegments[j-Count].Pos;
      FSegments[j].Dir := FSegments[j-Count].Dir;
    end;
  end;
begin
  Speed := 0.06+ FSpeed + Game.GameTime/1600000;
  Dir := Vec2f(Input.Mouse.Pos.x, Input.Mouse.Pos.y)+Game.Camera-FSegments[1].Pos;
  Rot := FSegments[1].Dir.Angle(Dir);
  FSegments[1].Dir := FSegments[1].Dir.Rotate( Sign(Rot)*Min(abs(Rot),(3+(Speed/0.2))*dt/1000)).Normal;


  FPhase := FPhase + Speed*dt;
  while FPhase > SEGMENT_DIST do
  begin
    FPhase := FPhase - SEGMENT_DIST;

    for I := FSegmentsCount downto 2 do
      FSegments[I].Pos := FSegments[I-1].Pos;

    FSegments[1].Pos := FSegments[1].Pos + FSegments[1].Dir*SEGMENT_DIST;
    FSegments[1].Dir := FSegments[1].Dir;

    for I := FSegmentsCount downto 2 do
      FSegments[I].Dir := (FSegments[I-1].Pos - FSegments[I].Pos).Normal;
  end;

  FTraceDist := FTraceDist + Speed*dt;
  while FTraceDist > 5.5 do
  begin
    FTraceDist := FTraceDist - 5.5;
    FTrace[1] := FSegments[1].Pos + FSegments[1].Dir * FPhase*0.5;
    for i := High(FTrace) downto 2 do
      FTrace[i] := FTrace[i-1];
  end;
              {
  for I := FSegmentsCount downto 2 do
  if (FSegments[I].Pos - FSegments[I-1].Pos).LengthQ > Sqr(SEGMENT_DIST) then
   begin
      Dir := (FSegments[I-1].Pos - FSegments[I].Pos).Normal {*((FSegments[I].Pos - FSegments[I-1].Pos).LengthQ/Sqr(SEGMENT_DIST))};
   {   FSegments[I].Pos := FSegments[I].Pos + Dir*Speed*dt;//SEGMENT_DIST;
      FSegments[I].Dir := Dir;
   end;           }

  for I := FSegmentsCount downto 5 do
    if ((FSegments[i].Pos-FSegments[1].Pos).LengthQ < Sqr(SEGMENT_DIST)) then
    Game.Quit;

  for i := 1 to Game.RockCount do
    if ((Game.Rock[i].Pos-FSegments[2].Pos).LengthQ < Sqr(EAT_DIST)) then
    begin
      if (FBerserkStart <> -1) then
      begin
        Game.EatRock(i);
        AddSegments(1);
        FLastEat := Utils.Time;
      end else
        Game.Quit;
      break;
    end;

  for i := 1 to Game.FoodCount do
    if ((Game.Food[i].Pos-FSegments[2].Pos).LengthQ < Sqr(EAT_DIST)) and (FSegmentsCount<High(FSegments)) then
    begin
      if (Game.Food[i].Kind = 1) or (Game.Food[i].Kind = 2) then
      begin
        AddSegments(Game.Food[i].Kind);
        FSpeed := FSpeed + Game.Food[i].Kind*0.003;
      end else
        FBerserkStart := Utils.Time;
      Game.EatFood(i);
      FLastEat := Utils.Time;
      break;
    end;

  if (FBerserkStart <> -1) and (Utils.Time - FBerserkStart > 15000) then
    FBerserkStart := -1;

  if (Utils.Time - FLastEat > 30000) then
    Game.Quit;

end;

procedure TWorm.OnRenderTrace;
var
  i : integer;
begin
  for i := 1 to High(FTrace) do
    Render2d.DrawSprite(FTraceTex, FTrace[i].x - Game.Camera.x - SEGMENT_SIZE/2, FTrace[i].y - Game.Camera.y- SEGMENT_SIZE/2, SEGMENT_SIZE, SEGMENT_SIZE, Vec4f(1, 1, 1, Min(1, (High(FTrace)-i)*4/High(FTrace))));
end;

procedure TWorm.OnRender;
var
  i     : integer;
  Pos   : TVec2f;
  Color : TVec4f;
begin
  for i := FSegmentsCount downto 1 do
  begin
    Pos := FSegments[i].Pos + FSegments[i].Dir * FPhase - Game.Camera;

    if (FBerserkStart = -1) then
      Color := clWhite.Lerp(Vec4f(0.2, 0.4, 0.2, 1), Max(Utils.Time - FLastEat - 20000, 0)/10000)
    else
      Color := Vec4f(1, 0.2, 0.2, 1).Lerp(clWhite, Sin(Max(Utils.Time - FBerserkStart - 12000, 0)*0.015));

    Render2d.DrawSprite(FTex[Min(i,2)],Pos.x - SEGMENT_SIZE/2, Pos.y - SEGMENT_SIZE/2, SEGMENT_SIZE, SEGMENT_SIZE, Color, -(FSegments[I].Dir.Lerp(FSegments[Max(I-1,1)].Dir, FPhase/SEGMENT_DIST).Angle(Vec2f(1,0)))*rad2deg);
  end;
end;

end.
