unit _Map;

interface

Uses
  QuadEngine,
  _Scene,
  _Forms,
  _Geometry,
  _Resources,
  _Camera,    
  _SysUtils,   
  Direct3d9;

const
  Max_Nodes = 550;
  Node_Size = 512;   
  Node_Real_Size = 460;
  Max_Craters = 512;

type
TCrater = class
    FPosition: TVector2;
    FSize: Single;
    FAngle: Single;
  private
    FNearestNodes: Array [0 .. 3] of TPoint;
    FNearestNodesCount: Byte;
  public      
    constructor Create(Position: TVector2; Angle: Single; Size: Single);
    procedure Draw(Shift: TPoint);
end;

TNode = class
    FAge: Byte;
    FInitiated: Boolean;
    FTexture:  IQuadTexture;     
    FPosition: TPoint;     
    class var FCrater: Array [0 .. Max_Craters] of TCrater;
    class var FCraterCount: Word;
  public
    NeedRegenerate: Boolean;
    constructor Create();             
    destructor  Destroy; override;
    class function AddCrater(Position: TVector2; Angle: Single; Size: Single): TCrater;
    procedure Generate(Position: TPoint);
    procedure DecreaseAge();
    procedure Draw;
    property Age:      Byte   read FAge;
    property Position: TPoint read FPosition;
end;

TMap = class
    FNode: Array [0 .. Max_Nodes] of TNode;
    FScreen: IQuadTexture;
    function FindNode(x, y: Integer): Boolean;
    procedure DrawNode(x, y: Integer);
  public
    constructor Create(QuadEngine: IQuadRender);
    destructor  Destroy; override;         
    procedure AddCrater(Position: TVector2; Angle: Single; Size: Single);
    procedure Render;
    procedure Update(dt: single);
  end;

implementation

{$REGION 'Crater'}
function FTrunc(A: Single): Integer;
begin
  if (A < 0) then
    Result:= Trunc(A) - 1
  else
    Result:= Trunc(A);
end;


constructor TCrater.Create;
var
  BX, BY,
  EX, EY: Integer;
begin
  FSize:= Size;     
  FAngle:= Angle;
  FPosition:= Position;
//  Window.Title:= IntToStr(Trunc(Size));
  BX:= FTrunc((Position.X - Size) / Node_Size);
  BY:= FTrunc((Position.Y - Size) / Node_Size);
  EX:= FTrunc((Position.X + Size) / Node_Size);
  EY:= FTrunc((Position.Y + Size) / Node_Size);

  FNearestNodes[0]:= Point(BX, BY);
  FNearestNodes[1]:= Point(EX, BY);
  FNearestNodes[2]:= Point(BX, EY);
  FNearestNodes[3]:= Point(EX, EY);
  FNearestNodesCount:= 3;
end;

procedure TCrater.Draw(Shift: TPoint);
begin
  Resources.CraterTexture.DrawRot(
    FPosition.X - Shift.X * Node_Size,
    FPosition.Y - Shift.Y * Node_Size,
    FAngle, FSize / 256 * 1.6, $44ffffff);
end;
{$ENDREGION}

{$REGION 'Nodes'}
constructor TNode.Create();
begin
  FPosition.X:= MaxInt;
  FPosition.Y:= MaxInt;
  FInitiated:= false;
  FCraterCount:= 0;
end;

destructor TNode.Destroy();
begin
  FTexture:= nil;
end;

class function TNode.AddCrater(Position: TVector2; Angle: Single; Size: Single): TCrater;
var
  i: Word;
begin
  FCrater[FCraterCount]:= TCrater.Create(Position, Angle, Size);    
  Result:= FCrater[FCraterCount];
  if (FCraterCount < Max_Craters) then
    Inc(FCraterCount);
end;

procedure TNode.Generate(Position: TPoint);
var
  i: Integer;    
  j: Byte;
begin
  NeedRegenerate:= false;
  FAge:= 255;
  FPosition:= Position;
  if (not FInitiated) then
    begin
      Camera.Quad.CreateRenderTexture(Node_Size, Node_Size, FTexture, 0);
      FInitiated:= True;
    end;                                            
  Camera.Quad.SetTextureAdressing(D3DTADDRESS_WRAP);
  Camera.Quad.RenderToTexture(True, FTexture, 0, true);
  Camera.Quad.Clear($ff000000);
  Resources.MapShift[0]:= FPosition.X;   
  Resources.MapShift[1]:= FPosition.Y;
  Resources.MapGenerationShader.SetShaderState(True);
  Resources.NoiseTexture.Draw(0, 0);
  Resources.MapGenerationShader.SetShaderState(False);
  for i:= 0 to FCraterCount - 1 do
    for j:= 0 to 3 do
      if FPosition = FCrater[i].FNearestNodes[j] then
        begin
          FCrater[i].Draw(FPosition);
        end;
    
//  Resources.Font.TextOut(0, 0, 0.4, PAnsiChar(IntToStr(FPosition.X) + ' ' + IntToStr(FPosition.Y)));
  Camera.Quad.RenderToTexture(False, FTexture);
  Camera.Quad.SetTextureAdressing(D3DTADDRESS_CLAMP);
end;

procedure TNode.DecreaseAge();
begin
  if (FAge > 0) then
    Dec(FAge); 
end;

procedure TNode.Draw;
begin              
  FAge:= 80;
  FTexture.DrawRot(
    Camera.GetX(FPosition.X * Node_Size),
    Camera.GetY(FPosition.Y * Node_Size),
    0,
    Camera.Scale);
end;
{$ENDREGION}

{$REGION 'Map'}
constructor TMap.Create(QuadEngine: IQuadRender);
var
  i: Word;
begin        
  Camera.Quad.CreateRenderTexture(Window.Width, Window.Height, FScreen, 0);

  for i:= 0 to Max_Nodes do
    FNode[i]:= TNode.Create;

  for i:= 0 to 40 do
    TNode.AddCrater(Vector2(Random(10000) - 5000, Random(10000) - 5000), Random(360), Random(200) + 100);
end;

destructor TMap.Destroy;  
var
  i: Word;
begin
  for i:= 0 to Max_Nodes do
    FNode[i].Destroy;      
  FScreen:= nil;
  Inherited;
end;

function TMap.FindNode(x, y: Integer): Boolean;
var
  i, Min: Word;
  Flag: Boolean;
begin
  Flag:= True;
  for i:= 0 to Max_Nodes do
    if (FNode[i].Position = Point(x, y)) then
      begin
        if (FNode[i].NeedRegenerate) then
          begin
            FNode[i].Generate(Point(x, y));
            Flag:= False;
          end
        else
          Flag:= False;
        Break;
      end;    
  Result:= not Flag;
  if Flag then
    for i:= 0 to Max_Nodes do
      if FNode[i].Age = 0 then
        begin
          FNode[i].Generate(Point(x, y));
          Flag:= False;
          Break;
        end;
  Min:= 0;
  if Flag then
    begin
      for i:= 1 to Max_Nodes do
        if FNode[i].Age < FNode[Min].Age then
          begin
            Min:= i;
          end;
      FNode[Min].Generate(Point(x, y));   
      Flag:= False;
    end;
end;

procedure TMap.DrawNode(x, y: Integer);
var
  i: Word;
begin
  for i:= 0 to Max_Nodes do
    if FNode[i].Position = Point(x, y) then
      begin
        FNode[i].Draw;
        Break;
      end;
end;

procedure TMap.Render;
var
  i, j: Integer;
  StartNode, EndNode: TPoint;
  Result: TVector2;
  Generated: Byte;
begin
  Result:= (Camera.Shift - Camera.Center / Camera.Scale) * (1 / Node_Size);
  StartNode.X:= Trunc(Result.X);
  StartNode.Y:= Trunc(Result.Y);
  Result:= (Camera.Shift + Camera.Center / Camera.Scale) * (1 / Node_Size);
  EndNode.X:= Trunc(Result.X);
  EndNode.Y:= Trunc(Result.Y);
  Generated:= 0;
  for i:= StartNode.X - 1 to EndNode.X + 1 do
    for j:= StartNode.Y - 1 to EndNode.Y + 1 do
      begin
        if( not FindNode(i, j))then
          Inc(Generated);
        if (Generated > 2) then
          break;
      end;
//  Camera.Quad.RenderToTexture(True, FScreen, 0, true);
  Resources.MapLightningShader.SetShaderState(True);
  for i:= StartNode.X - 1 to EndNode.X + 1 do
    for j:= StartNode.Y - 1 to EndNode.Y + 1 do
      DrawNode(i, j);
  Resources.MapLightningShader.SetShaderState(False);
 // Camera.Quad.RenderToTexture(False, FScreen);
 // FScreen.Draw(0, 0);
end;

procedure TMap.AddCrater(Position: TVector2; Angle: Single; Size: Single);
var
  i, j: Word;
  Crater: TCrater;
begin
  Crater:= TNode.AddCrater(Position, Angle, Size);
  for j:= 0 to 3 do
    for i:= 0 to Max_Nodes do
      if FNode[i].Position = Crater.FNearestNodes[j] then
        begin
          FNode[i].NeedRegenerate:= True;
        end;
end;

procedure TMap.Update(dt: single);
var
  i: Word;
begin
  for i:= 0 to Max_Nodes do
    FNode[i].DecreaseAge;   
end;  
{$ENDREGION}

end.
