unit _World;

interface

Uses
  QuadEngine,
  _Scene,
  _Geometry,
  _Resources,
  _Forms,
  _SysUtils,
  _Objects,    
  pngImage,
  Classes;

const
//  DUMMY: PAnsiChar = '0';
  MAP_SIZE = 1024;
  TURN_TIME = 4; //in seconds
  SELECTOR_COLOR = $300098d7;
  
type

TRubberRect = record
    BeginPoint: TVector2;
    EndPoint:   TVector2;
  end;

TWorld = class;

TSelector = class
  private
    FQuad: IQuadRender;
    TopLeftPoint: TVector2;
    BottomRightPoint: TVector2;
    Active: Boolean;      
    Command: Boolean;
    Selected: TList;
  public
    constructor Create(QuadEngine: IQuadRender);
    procedure Clear;
    procedure Render;
    procedure Control(SelectedObject: TMapObject);
  end;

TWorld = class(TBasicScene)
  private
    FSelector: TSelector;
    FQuad: IQuadRender;
    Camera,
    CameraPointer: TVector2;
    CenterShift: TVector2;
    CameraBorder: array [0..1] of TVector2;
    Zoom,
    ViewZoom,
    ZoomPointer: Single;
    ZoomBorder: array [0..1] of Single;
    Velosity: TVector2;
    Music: Byte;
    MapManager: TMapManager;

    procedure MoveCamera(const x, y: Single);
    procedure ChangeZoom(k: Single);      
    procedure LoadMap(MarkerImage, LevelImage: String);
    procedure Reinit; 
  public
    constructor Create(QuadEngine: IQuadRender);
    destructor Destroy; override;
    procedure Render; override;
    procedure Update(dt: single); override;
    procedure Control; override;
    procedure ChangeResolution;
    procedure StartLevel(id: Integer);
  end;

var
  World:TWorld;

implementation
//  Selector
{$REGION 'Selector'}
constructor TSelector.Create(QuadEngine: IQuadRender);
begin
  FQuad:= QuadEngine;
  TopLeftPoint:=     Vector2(0, 0);
  BottomRightPoint:= Vector2(0, 0);
  Active:= false;
  Selected:= TList.Create;
  Command:= false;
end;

procedure TSelector.Clear;
begin
  TopLeftPoint:=     Vector2(0, 0);
  BottomRightPoint:= Vector2(0, 0);
  Active:= false;
  Selected.Destroy;
  Selected:= TList.Create;
  Command:= false;
end;

procedure TSelector.Render;
begin
  if Active then
    begin
      FQuad.Rectangle(
        TopLeftPoint.X, TopLeftPoint.Y,
        BottomRightPoint.X, BottomRightPoint.Y,
        SELECTOR_COLOR);    
      FQuad.Rectangle(
        TopLeftPoint.X, BottomRightPoint.Y,
        BottomRightPoint.X, TopLeftPoint.Y,
        SELECTOR_COLOR);
    end;
end;

procedure TSelector.Control(SelectedObject: TMapObject);
var
  i: Word;
  Point: TVector2;
begin                 
  BottomRightPoint:= Vector2(Window.Mouse.X, Window.Mouse.Y);
  if Window.Mouse.mbl then
    if not Active then
      begin
        TopLeftPoint:= Vector2(Window.Mouse.X, Window.Mouse.Y);
        Active:= true;
      end;
  if not Window.Mouse.mbl and Active then
    begin
      Selected.Free;
      Selected:= World.MapManager.SelectRect(TopLeftPoint, BottomRightPoint);
    end;

  if not Window.Mouse.mbl then
    Active:= false;

  if (not Window.Mouse.mbr) and Command then
    begin
      Point:= (Vector2(Window.Mouse.X, Window.Mouse.Y) / World.Zoom - World.Camera);
      if Selected.Count>0 then
        for i:=0 to Selected.Count - 1 do
          if Selected[i]<> nil then
            begin
              if SelectedObject<>nil then
                begin
                  if TMapObject(Selected[i]) is TInterceptor then
                    TInterceptor(Selected[i]).DockToObj(SelectedObject);
                  if TMapObject(Selected[i]) is THelicopter then
                    THelicopter(Selected[i]).DockToObj(SelectedObject);
                end
              else
                begin
                  if TMapObject(Selected[i]) is TInterceptor then
                    TInterceptor(Selected[i]).FlyToPoint(Point);
                  if TMapObject(Selected[i]) is THelicopter then
                    THelicopter(Selected[i]).FlyToPoint(Point);
                end;
            end;

    end;
  Command:= Window.Mouse.mbr;
end;
{$ENDREGION}
//  World
{$REGION 'World'}
procedure TWorld.Reinit;
begin
  //Camera
  CameraBorder[0]:= Vector2( -MAP_SIZE / 2, -MAP_SIZE / 2);
  CameraBorder[1]:= Vector2(  MAP_SIZE / 2,  MAP_SIZE / 2); 
  CameraPointer:= Vector2(-200,-200);
  CenterShift:= Vector2( Window.Width / 2, Window.Height / 2);
  //Zoom
  ZoomPointer:= 4;
  Zoom:= 1;
  ViewZoom:= 1;
  ZoomBorder[0]:= 1;
  ZoomBorder[1]:= 25;
  FSelector.Clear;
  if MapManager<>nil then
    MapManager.Destroy;
  MapManager:= TMapManager.Create;
end;

constructor TWorld.Create;
begin
  FQuad:= QuadEngine;
  FQuad.SetBlendMode(qbmSrcAlpha);
  FSelector:= TSelector.Create(FQuad);
  //Objects    
  TMapObject.SetCameraAndZoom(Camera, Zoom, ViewZoom);
  StartLevel(0);
end;

procedure TWorld.StartLevel(id: Integer);
begin
  Reinit;
  case id of
    0:begin
        LoadMap('Data/Maps/Info_1.png', LEVEL_1);
        Music:= 1;
      end;
    1:begin
        LoadMap('Data/Maps/Info_2.png', LEVEL_2);
        Music:= 2;
      end;
    2:begin
        LoadMap('Data/Maps/Info_3.png', LEVEL_3);
        Music:= 3;
      end;
    3:begin
        LoadMap('Data/Maps/Info_4.png', LEVEL_4);
        Music:= 1;
      end;
  end;
  if (MapManager.Player[S_PLAYER].MainBase<> nil) then
    CameraPointer:= -TBase(MapManager.Player[S_PLAYER].MainBase).Position;
  Camera:= CameraPointer;
end;

destructor TWorld.Destroy;
begin
  Inherited;
  MapManager.Free;
  FSelector.Free;
end;

procedure TWorld.MoveCamera(const x, y: Single);
begin
  Velosity:= Velosity + Vector2(x, y);
end;

procedure TWorld.Control;
var
  Speed: Single;
begin
//Control map Objects
  MapManager.Control;
//Selector - Rubber rect
  FSelector.Control(MapManager.SelectedObject);
// Camera move
  Speed:= 5 / Zoom;
  Velosity:= Vector2(0, 0);
  if (Window.key[KEY_D] or Window.key[KEY_RIGHT]) then begin MoveCamera(-Speed, 0); end;
  if (Window.key[KEY_A] or Window.key[KEY_LEFT ]) then begin MoveCamera( Speed, 0); end;
  if (Window.key[KEY_S] or Window.key[KEY_DOWN ]) then begin MoveCamera(0, -Speed); end;
  if (Window.key[KEY_W] or Window.key[KEY_UP   ]) then begin MoveCamera(0,  Speed); end;

  if Window.Mouse.x < Window.Width * 0.03 then
    MoveCamera( Speed, 0);
  if Window.Mouse.x > Window.Width * 0.97 then
    MoveCamera(-Speed, 0);
  if Window.Mouse.Y < Window.Height * 0.03 then
    MoveCamera( 0,  Speed);
  if Window.Mouse.Y > Window.Height * 0.97 then
    MoveCamera( 0, -Speed);
// Camera Zoom
  if (Window.key[KEY_PLUS] or Window.key[KEY_ADD_PLUS]) then
    ChangeZoom(1.01);
  if (Window.key[KEY_MINUS] or Window.key[KEY_ADD_MINUS]) then
    ChangeZoom(0.987);
  if (Window.Mouse.Wheel>0) then
    ChangeZoom(1.2);
  if (Window.Mouse.Wheel<0) then  
    ChangeZoom(0.77);
  Window.Mouse.Wheel:= 0;
// Functional  
  if Window.key[KEY_TILDA] or Window.key[KEY_ESC] then  //esc
  begin
    Window.key[KEY_TILDA]:= false;
    Window.key[KEY_ESC]:= false;
    Menu.ReturnToMainMenu;
    Manager.SetScene(Menu);
  end;
end;


procedure TWorld.Render;
var
  i:Word;
begin
  //Clear
  FQuad.Clear($ff3d5965);
//todo  
//  FQuad.Clear($ff000000);
  //Map
  Resources.TileTexture.DrawRot(Camera.X * Zoom, Camera.Y * Zoom, 0, Zoom);
  MapManager.Render;
  //Interface
  Resources.Font.TextOutAligned(
    Window.Width - 100, 50, 1,
    PAnsiChar('$ ' + IntToStr(MapManager.Player[1].Credits)),
    $ffffffff,
    qfaRight);
  //todo debug
 { Resources.Font.TextOut(
    100, 100, 1,
    PAnsiChar('$ ' + IntToStr(MapManager.Player[2].Credits)), 
    $ffffffff);      }
  FSelector.Render;
end;

procedure TWorld.Update(dt: Single);
var
  i:Word;
begin          
  Resources.PlayMusic(Music);
  MapManager.Update(dt);
  //Move CameraPointer
  CameraPointer:= Velosity + CameraPointer;
  //Collide CameraPointer with borders
  if CameraPointer.X < CameraBorder[0].X then
    CameraPointer.X:= CameraBorder[0].X;
  if CameraPointer.Y < CameraBorder[0].Y then
    CameraPointer.Y:= CameraBorder[0].Y;
  if CameraPointer.X > CameraBorder[1].X then
    CameraPointer.X:= CameraBorder[1].X;
  if CameraPointer.Y > CameraBorder[1].Y then
    CameraPointer.Y:= CameraBorder[1].Y;

  //Move
  Camera:= Camera * 0.9 + (CameraPointer + CenterShift / Zoom) * 0.1;
  //Zoom
  Camera:= Camera - CenterShift / Zoom;
  Zoom:= Zoom * 0.9 + ZoomPointer * 0.1;
  ViewZoom:= Zoom;
  if ViewZoom < ZOOM_LIMITER then
    ViewZoom:= ZOOM_LIMITER;
  Camera:= Camera + CenterShift / Zoom;
end;

procedure TWorld.ChangeZoom(k: Single);
begin
  ZoomPointer:= ZoomPointer * k;  
  if ZoomPointer < ZoomBorder[0] then
    ZoomPointer:= ZoomBorder[0];
  if ZoomPointer > ZoomBorder[1] then
    ZoomPointer:= ZoomBorder[1];
end;

procedure TWorld.ChangeResolution;
begin
  {Screen:= nil;
  FQuad.CreateRenderTexture(Window.Width, Window.Height, Screen, 0);  }
end;
{$ENDREGION}

{$REGION 'Load Map'}
procedure TWorld.LoadMap(MarkerImage, LevelImage: String);
var
  i, j,
  CarCount,
  WayPointCount: Word;
  mapImage: TpngObject;
begin
  mapImage:=TpngObject.Create;
  mapImage.LoadFromFile(MarkerImage);
  Resources.LoadMap(LevelImage);
  for i:= 0 to mapImage.Width - 1 do
    for j:= 0 to mapImage.Height - 1 do
    case mapImage.Pixels[i,j] of
      $000000: ;//none
      $ffffff: MapManager.AddMapObject(TResFactory.Create(Vector2(i, j) - Vector2(512, 512)));//factory
      $ff0000: MapManager.AddMapObject(TBase.Create(Vector2(i, j) - Vector2(512, 512), 1));//blue base
      $0000ff: MapManager.AddMapObject(TBase.Create(Vector2(i, j) - Vector2(512, 512), 2));//red base
      $00ff00: MapManager.AddMapObject(TBase.Create(Vector2(i, j) - Vector2(512, 512), 3));//green base
    end;
end;
{$ENDREGION}
end.
