unit GameMode.Planet;

interface

uses
  System.Classes, System.Generics.Collections, System.SysUtils, QuadEngine, GameMode.Custom, Vec2f, ObjectIPADConsole,
  Math, Units.Player, Windows, Level.Map, Units.Custom, CustomObject, Subject, CustomBullet, QuadFX, GlobalTypes;

type
  TGameModePlanet = class(TGameModeCustom)
  private
    FUnitsList: TList<TCustomUnit>;
    FActionPlayer: TUnitsPlayer;
    FLevelMap: TLevelMap;
    FLasso: TUnitsPlayer;
    FLassoWidth: Integer;
    FMousePosition: TVec2f;
    FHeroPanelLinePosition: Single;
    FInterfaceMouseClick: Boolean;
    FSquad: array[0..3] of TUnitsPlayer;
    FIPADConsole: TList<TObjectIPADConsole>;
    procedure SetMainUnit(AUnit: TUnitsPlayer);
    procedure Collides;
    function GetMousePositionToCamera: TVec2f;
  public
    constructor Create(AWorldType: TWorldType);
    destructor Destroy; override;
    procedure Init;
    procedure Process(const Delta: Double); override;
    procedure NextHero;
    procedure Draw; override;
    property ActionPlayer: TUnitsPlayer read FActionPlayer;
    property LevelMap: TLevelMap read FLevelMap;
    property UnitsList: TList<TCustomUnit> read FUnitsList;
  end;

implementation

uses
  Resources, CustomSubject, GameOverlay, Collision, ObjectConsole, ObjectStartPoint;

constructor TGameModePlanet.Create(AWorldType: TWorldType);
begin
  inherited Create;
  FHeroPanelLinePosition := -150;
  FLasso := nil;
  FLassoWidth := 256;
  TCustomObject.ObjectList.Clear;
  FLevelMap := TLevelMap.Create('Maps/002', AWorldType);
  FUnitsList := TList<TCustomUnit>.Create;
  FIPADConsole := TList<TObjectIPADConsole>.Create;

  TGlobals.QuadCamera.SetPosition(FLevelMap.StartPosition - TVec2f.Create(TGlobals.GWidth / 2, TGlobals.GHeight / 2));
  TGlobals.QuadCamera.SetAngle(FLevelMap.Angle);
                      {
  TGameOverlay.AddDialog(1, 'Тут что-то нечисто! Жопой чую!', lEarth);
  TGameOverlay.AddDialog(2, 'CODE. MOTHERFUCKER!', lAncient);
  TGameOverlay.AddDialog(1, 'Что это значит, блин?', lEarth);
  TGameOverlay.AddDialog(2, 'Кодь, блеать!', lEarth);   }
end;

procedure TGameModePlanet.Init;
var
  i: Integer;
  Vector: TVec2f;
  Angle: Single;
begin
  for i := 0 to 3 do
    FSquad[i] := nil;

  FInterfaceMouseClick := TGlobals.QuadInput.IsMouseDown(mbLeft);

  Angle := DegToRad(LevelMap.StartObj.Angle - 30);
  for i := 0 to TGlobals.LevelHeroes.Count - 1 do
  begin
    FSquad[i] := TGlobals.LevelHeroes[i];
    Vector := TVec2f.Create(cos(Angle), sin(Angle)) * 96;
    TGlobals.LevelHeroes[i].Position := FLevelMap.StartPosition + Vector {- Vector.Normalize * i * 24};

    FUnitsList.Add(TGlobals.LevelHeroes[i]);
    TGlobals.LevelHeroes[i].NextGun;

    Angle := Angle + DegToRad(20);
   if i > 0 then
    begin
      TGlobals.LevelHeroes[i].Parent := TGlobals.LevelHeroes[i - 1];
      TGlobals.LevelHeroes[i - 1].Child := TGlobals.LevelHeroes[i];
    end;
  end;

  FActionPlayer := TGlobals.LevelHeroes[0];
end;

destructor TGameModePlanet.Destroy;
var
  i: Integer;
begin
  for i := TCustomObject.ObjectList.Count - 1 downto 0 do
    TCustomObject.ObjectList[i].Free;
  TCustomObject.ObjectList.Clear;
  FUnitsList.Free;
  FLevelMap.Free;
  FIPADConsole.Free;
  inherited;
end;

procedure TGameModePlanet.SetMainUnit(AUnit: TUnitsPlayer);
var
  i: Integer;
  SortItim: TUnitsPlayer;
  SortList: TList<TUnitsPlayer>;
begin
  if not Assigned(AUnit) then
    Exit;

  if Assigned(AUnit.Child) or Assigned(AUnit.Parent) then
  begin
    SortList := TList<TUnitsPlayer>.Create;
    SortList.Add(AUnit);
    SortItim := AUnit.Child;
    while Assigned(SortItim) do
    begin
      SortList.Add(SortItim);
      SortItim := SortItim.Child;
    end;

    SortItim := AUnit.Parent;
    while Assigned(SortItim) do
    begin
      SortList.Add(SortItim);
      SortItim := SortItim.Parent;
    end;
    FActionPlayer := SortList[0];
    SortList[0].Parent := nil;
    SortList[SortList.Count - 1].Child := nil;

    for i := 1 to SortList.Count - 1 do
    begin
      SortList[i].Parent := SortList[i - 1];
      SortList[i - 1].Child := SortList[i];
    end;
    SortList.Free;
  end
  else
    FActionPlayer := AUnit;
end;

procedure TGameModePlanet.NextHero;
var
  PlayerUnit: TUnitsPlayer;
begin
  if not Assigned(FActionPlayer.Child) then
  begin
    PlayerUnit := FActionPlayer;
    while Assigned(PlayerUnit.Parent) do
      PlayerUnit := PlayerUnit.Parent;
    FActionPlayer := PlayerUnit; SetMainUnit(PlayerUnit);
  end
  else
    SetMainUnit(FActionPlayer.Child);
end;

procedure TGameModePlanet.Process(const Delta: Double);
  procedure SetActionSubject(ASubject: TSubject);
  var
    i: Integer;
  begin
    for i := 0 to ActionPlayer.Inventory - 1 do
      if Assigned(ASubject) and (ActionPlayer.Subjects[i] <> ASubject) and (ActionPlayer.Subjects[i].SubjectType = ASubject.SubjectType) then
        ActionPlayer.Subjects[i].IsAction := False;

    ASubject.IsAction := not ASubject.IsAction;
  end;

  procedure FindConsole;
  var
    X, Y: Integer;
    Range: TMapRange;
  begin
    FIPADConsole.Clear;
    if not Assigned(ActionPlayer) then
      Exit;

    Range := FLevelMap.GetMapRange(
      ActionPlayer.Position - TVec2f.Create(150, 150),
      ActionPlayer.Position + TVec2f.Create(150, 150)
    );

    for Y := Range.A.Y to Range.B.Y do
      for X := Range.A.X to Range.B.X do
        if Assigned(LevelMap.Fields[X, Y].Obj) and (LevelMap.Fields[X, Y].Obj is TObjectIPADConsole) and LevelMap.Fields[X, Y].Obj.ParentAction then
          FIPADConsole.Add(TObjectIPADConsole(LevelMap.Fields[X, Y].Obj));
  end;

var
  i, j: Integer;
  AngleRad: Single;
  Vector: TVec2f;
  VectorCamera: TVec2f;
  PlayerUnit: TUnitsPlayer;
  Field: PField;
  Rect: T2DRect;
  AUnit: TCustomUnit;
begin
  if LevelMap.StartObj.Action then
  begin
    TGlobals.GameModeManager.SetActiveGameMode(TGlobals.GameModes.FeesSquad);
    Exit;
  end;

  TEffects.QuadFXLayer.Update(Delta);

  if FInterfaceMouseClick and not TGlobals.QuadInput.IsMouseDown(mbLeft) then
    FInterfaceMouseClick := False;
  FHeroPanelLinePosition := FHeroPanelLinePosition + Delta * 50;

  if FHeroPanelLinePosition > 500 then
    FHeroPanelLinePosition := FHeroPanelLinePosition - 600;

  if TGlobals.LevelHeroes.Count > 0 then
  begin
    FMousePosition := GetMousePositionToCamera;

    if TGlobals.QuadInput.IsMouseClick(mbRight) and Assigned(ActionPlayer) and not ActionPlayer.IsNeedToKill then
    begin
      Field := FLevelMap.GetFieldToPosition(FMousePosition);
      if Assigned(Field) and Assigned(Field.Obj) and Field.Obj.ParentAction and (ActionPlayer.Position.Distance(Field.Position * 24 + TVec2f.Create(12, 12)) < 32)
        and TCollision.PointCircle(FMousePosition, T2DCircle.Create(Field.Position * 24 + TVec2f.Create(12, 12), 12)) then
        begin
          if Field.Obj is TCustomSubject then
            ActionPlayer.PickUp(TCustomSubject(Field.Obj))
          else
            if Field.Obj is TObjectConsole then
              TObjectConsole(Field.Obj).SetHero(ActionPlayer)
            else
              if Field.Obj is TObjectStartPoint then
                TObjectStartPoint(Field.Obj).SetHero(ActionPlayer);
        end;
    end;

    if not Assigned(FLasso) and TGlobals.QuadInput.IsMouseClick(mbLeft) then
    begin
      for i := 0 to TGlobals.LevelHeroes.Count - 1 do
        if TGlobals.LevelHeroes[i].Position.Distance(FMousePosition) < TGlobals.LevelHeroes[i].Radius then
        begin
          FLasso := TGlobals.LevelHeroes[i];
          Break;
        end;
    end;

    if Assigned(FLasso) then
      FInterfaceMouseClick := True;

    if Assigned(FLasso) and not TGlobals.QuadInput.IsMouseDown(mbLeft) then
    begin
      if FLasso.Position.Distance(FMousePosition) < FLassoWidth then
        for i := 0 to TGlobals.LevelHeroes.Count - 1 do
          if (TGlobals.LevelHeroes[i] <> FLasso) and (TGlobals.LevelHeroes[i].Position.Distance(FMousePosition) < TGlobals.LevelHeroes[i].Radius) then
          begin
            case random(10) of
              0: TGameOverlay.AddDialog(FLasso.Portrait, 'Присоединяюсь', lEarth);
              1: TGameOverlay.AddDialog(FLasso.Portrait, 'Перегруппировываемся', lEarth);
              2: TGameOverlay.AddDialog(FLasso.Portrait, 'Иду к остальным', lEarth);
            end;
            FLasso.Join(TGlobals.LevelHeroes[i]);
            Break;
          end;
      FLasso := nil;
    end;

    if TGlobals.QuadInput.IsKeyPress(VK_TAB) then
      NextHero;

    if TGlobals.QuadInput.IsKeyPress(ord('1')) and Assigned(FSquad[0]) then
      SetMainUnit(FSquad[0]);

    if TGlobals.QuadInput.IsKeyPress(ord('2')) and Assigned(FSquad[1]) then
      SetMainUnit(FSquad[1]);

    if TGlobals.QuadInput.IsKeyPress(ord('3')) and Assigned(FSquad[2]) then
      SetMainUnit(FSquad[2]);

    if TGlobals.QuadInput.IsKeyPress(ord('4')) and Assigned(FSquad[3]) then
      SetMainUnit(FSquad[3]);

    if TGlobals.QuadInput.IsKeyPress(ord('Q')) then
    begin
      ActionPlayer.Join(nil);
      case random(10) of
        0: TGameOverlay.AddDialog(ActionPlayer.Portrait, 'Отсоединяюсь', lEarth);
        1: TGameOverlay.AddDialog(ActionPlayer.Portrait, 'Ждите здесь', lEarth);
        2: TGameOverlay.AddDialog(ActionPlayer.Portrait, 'Дальше я пойду один', lEarth);
      end;
    end;

    if TGlobals.QuadInput.IsMouseClick(mbLeft) then
    begin
      Rect := T2DRect.Create(TVec2f.Zero, TVec2f.Create(60, 74));
      for i := 0 to 3 do
        if Assigned(FSquad[i]) then
        begin
          if FSquad[i] = ActionPlayer then
            Rect.Size := TVec2f.Create(80, 100)
          else
            Rect.Size := TVec2f.Create(60, 74);

          if TCollision.PointRect(TGlobals.QuadInput.GetMousePosition, Rect) then
          begin
            FInterfaceMouseClick := True;
            if (FSquad[i] <> ActionPlayer) {and not FSquad[i].IsNeedToKill} then
              SetMainUnit(FSquad[i]);
            Break;
          end;
          Rect.Position.X := Rect.Position.X + Rect.Size.X;
        end;
    end;

    if Assigned(ActionPlayer) and not ActionPlayer.IsNeedToKill then
    begin
      with ActionPlayer do
      begin
        if TGlobals.QuadInput.IsKeyPress(ord('E')) then
          NextGun;

        if TGlobals.QuadInput.IsKeyPress(ord('R')) and Assigned(ActionPlayer.GunAction) and Assigned(ActionPlayer.GunAction.Gun) then
          ActionPlayer.GunAction.Gun.Reloading;

        if TGlobals.QuadInput.IsKeyPress(VK_DELETE) then
          ActionPlayer.Damage(1000);

        AngleRad := -FLevelMap.Angle * (pi / 180);
        VectorCamera := TVec2f.Create(cos(AngleRad), sin(AngleRad));

        Vector := TVec2f.Zero;
        if TGlobals.QuadInput.IsKeyDown(ord('W')) then
          Vector := Vector + VectorCamera.Normal;

        if TGlobals.QuadInput.IsKeyDown(ord('S')) then
          Vector := Vector - VectorCamera.Normal;

        if TGlobals.QuadInput.IsKeyDown(ord('A')) then
          Vector := Vector - VectorCamera;

        if TGlobals.QuadInput.IsKeyDown(ord('D')) then
          Vector := Vector + VectorCamera;

        if Vector <> TVec2f.Zero then
          Position := Position + Vector.Normalize * Speed * Delta;

        TGlobals.QuadCamera.SetPosition(Position - TVec2f.Create(TGlobals.GWidth / 2, TGlobals.GHeight / 2));
      end;
      PlayerUnit := ActionPlayer;
      while Assigned(PlayerUnit) do
      begin
        PlayerUnit.ViewVector := FMousePosition - PlayerUnit.Position;
        PlayerUnit := PlayerUnit.Child;
      end;
    end;
  end;

  for i := 0 to TCustomObject.ObjectList.Count - 1 do
    TCustomObject.ObjectList[i].Process(Delta);
  FindConsole;

  Vector := TVec2f.Create(5, TGlobals.GHeight - 96);
  if Assigned(ActionPlayer) and TGlobals.QuadInput.IsMouseClick(mbLeft) and (FIPADConsole.Count > 0) then
  begin
    if not ActionPlayer.IsNeedToKill and Assigned(ActionPlayer.GunAction) and Assigned(ActionPlayer.GunAction.Gun) then
      Vector.Y := Vector.Y - 32;

    for i := 0 to FIPADConsole.Count - 1 do
      if TCollision.PointRect(TGlobals.QuadInput.GetMousePosition, T2DRect.Create(Vector.X, Vector.Y - 32 * i, 32, 32)) then
      begin
        FIPADConsole[i].SetHero(ActionPlayer);
        FInterfaceMouseClick := True;
        Break;
      end;
  end;

  for i := 0 to ActionPlayer.Inventory - 1 do
    if Assigned(ActionPlayer.Subjects[i]) and TGlobals.QuadInput.IsMouseClick(mbRight)
      and TCollision.PointRect(TGlobals.QuadInput.GetMousePosition, T2DRect.Create(64 * i, TGlobals.GHeight - 64, 64, 64)) then
      case ActionPlayer.Subjects[i].SubjectType of
        stSpaceSuit, stFlashLight, stIPAD: SetActionSubject(ActionPlayer.Subjects[i]);
      end;

  TCustomBullet.ProcessList(Delta);

  if not FInterfaceMouseClick and Assigned(ActionPlayer) and not ActionPlayer.IsNeedToKill and Assigned(ActionPlayer.GunAction) and Assigned(ActionPlayer.GunAction.Gun) then
  begin
    if TGlobals.QuadInput.IsMouseClick(mbLeft)
      and TCollision.PointRect(TGlobals.QuadInput.GetMousePosition, T2DRect.Create(0, TGlobals.GHeight - 64, 64 * ActionPlayer.Inventory, 64)) then
      FInterfaceMouseClick := True
    else
      if TGlobals.QuadInput.IsMouseDown(mbLeft) then
        ActionPlayer.GunAction.Gun.Fire(ActionPlayer);
  end;

  for i := FUnitsList.Count - 1 downto 0 do
    if FUnitsList[i].IsNeedToKill then
    begin
      if FUnitsList[i] is TUnitsPlayer then
      begin
        TUnitsPlayer(FUnitsList[i]).Join(nil);
        TGlobals.LevelHeroes.Remove(TUnitsPlayer(FUnitsList[i]));
        TGlobals.Heroes.Remove(TUnitsPlayer(FUnitsList[i]));
        if Assigned(TUnitsPlayer(FUnitsList[i]).Child) or Assigned(TUnitsPlayer(FUnitsList[i]).Parent) then
          NextHero
        else
          if TGlobals.LevelHeroes.Count > 0 then
            SetMainUnit(TGlobals.LevelHeroes[0])
          else
            TGlobals.GameModeManager.SetActiveGameMode(TGlobals.GameModes.FeesSquad);
      end
      else
        FUnitsList[i].Free;
      FUnitsList.Delete(i);
    end
    else
      FUnitsList[i].Process(Delta);

  Collides;
end;

procedure TGameModePlanet.Draw;

  procedure LeftPanelDraw;
  var
    i: Integer;
    position: TVec2f;
  begin
    position := TVec2f.Zero;
    for i := 0 to 3 do
    begin
      if FSquad[i] = ActionPlayer then
      begin
        if not FSquad[i].IsNeedToKill then
        begin
          TTextures.HP.DrawMap(
            position + TVec2f.Create(3, 88), position + TVec2f.Create(3 + 74 * (FSquad[i].HP / FSquad[i].MaxHP), 120),
            TVec2f.Create(0.0078125, 0.5), TVec2f.Create(0.0078125 + 0.578125 * (FSquad[i].HP / FSquad[i].MaxHP), 1)
          );
          TTextures.HP.DrawMap(
            position + TVec2f.Create(2, 88), position + TVec2f.Create(130, 120),
            TVec2f.Create(0, 0), TVec2f.Create(1, 0.5)
          );
          TTextures.Portraits.DrawRotFrame(position + TVec2f.Create(40, 50), 0, 1, ActionPlayer.Portrait);
        end
        else
          TTextures.Portraits.DrawRotFrame(position + TVec2f.Create(40, 50), 0, 1, ActionPlayer.Portrait, $FFFF6666);

        position.X := position.X + 80;
      end
      else
      begin
        if Assigned(FSquad[i]) then
        begin
          if not FSquad[i].IsNeedToKill then
          begin
            TTextures.HP.DrawMap(
              position + TVec2f.Create(2, 63), position + TVec2f.Create(2 + 56 * (FSquad[i].HP / FSquad[i].MaxHP), 87),
              TVec2f.Create(0.0078125, 0.5), TVec2f.Create(0.0078125 + 0.578125 * (FSquad[i].HP / FSquad[i].MaxHP), 1)
            );
            TTextures.HP.DrawMap(
              position + TVec2f.Create(1, 63), position + TVec2f.Create(98, 87),
              TVec2f.Create(0, 0), TVec2f.Create(1, 0.5)
            );
            TTextures.Portraits.DrawRotFrame(position + TVec2f.Create(30, 38), 0, 0.75, FSquad[i].Portrait);
          end
          else
            TTextures.Portraits.DrawRotFrame(position + TVec2f.Create(30, 38), 0, 0.75, FSquad[i].Portrait, $FFFF6666);
        end;
        position.X := position.X + 60;
      end;
    end;

    for i := 0 to ActionPlayer.Inventory - 1 do
      if ActionPlayer.Subjects[i] = ActionPlayer.GunAction then
        TSubject.Draw(ActionPlayer.Subjects[i], TVec2f.Create(64 * i, TGlobals.GHeight - 64), sdtAction)
      else
        TSubject.Draw(ActionPlayer.Subjects[i], TVec2f.Create(64 * i, TGlobals.GHeight - 64));

    position := TVec2f.Create(5, TGlobals.GHeight - 96);
    if Assigned(ActionPlayer) and Assigned(ActionPlayer.GunAction) and Assigned(ActionPlayer.GunAction.Gun) then
    begin
      ActionPlayer.GunAction.Gun.Draw;
      position.Y := position.Y - 32;
    end;

    if ActionPlayer.ExistsSubject(stIPAD, True) then
      if FIPADConsole.Count > 0 then
      begin
        for i := 0 to FIPADConsole.Count - 1 do
        begin
          TGlobals.QuadRender.Rectangle(position + TVec2f.Create(0, 32 - 32 * FIPADConsole[i].Progress), position + TVec2f.Create(32, 32), $FF009900);
          if FIPADConsole[i].Action then
            TTextures.wifi.Draw(position, $FF009900)
          else
            TTextures.wifi.Draw(position);
          TGlobals.EarthFont.TextOut(position + TVec2f.Create(36, 22), 0.5, PWideChar(FIPADConsole[i].Name));
          position.Y := position.Y - 32;
        end;
      end
      else
        TGlobals.EarthFont.TextOut(position + TVec2f.Create(4, 22), 0.5, 'Нет сигнала');
  end;

  procedure DrawEffects;
  begin
    TEffects.QuadFXLayer.Draw;
  end;

  procedure DrawEffectsLight;
  var
    i: Integer;
    Effect: IQuadFXEffect;
    EffectParams: IQuadFXEffectParams;
  begin
    for i := 0 to TEffects.QuadFXLayer.GetEffectCount - 1 do
      if (TEffects.QuadFXLayer.GetEffect(i, Effect) = S_OK) and (Effect.GetEffectParams(EffectParams) = S_OK) then
      begin
        if (EffectParams = TEffects.Explosion) and (Effect.GetLife < 0.55) then
          TTextures.QuadGBuffer.DrawLight(TVec3f.Create(
            (Effect.GetPosition.X - TGlobals.QuadCamera.GetPosition.X) / TGlobals.GWidth,
            (Effect.GetPosition.Y - TGlobals.QuadCamera.GetPosition.Y) / TGlobals.GHeight,
             1
          ), 0.7, $FFFF8800)
        else
          if (EffectParams = TEffects.BulletShot) then
            TTextures.QuadGBuffer.DrawLight(TVec3f.Create(
              (Effect.GetPosition.X - TGlobals.QuadCamera.GetPosition.X) / TGlobals.GWidth,
              (Effect.GetPosition.Y - TGlobals.QuadCamera.GetPosition.Y) / TGlobals.GHeight,
               0.01
            ), 0.1, $11FF8800);

      end;
  end;

var
  X, Y, i, j: Integer;
  Position: TVec2f;
  size: TVec2f;
begin
  if Assigned(TTextures.WorldTexture[LevelMap.WorldType].Bg) then
    TTextures.WorldTexture[LevelMap.WorldType].Bg.Draw(TVec2f.Zero);

  TGlobals.QuadCamera.Enable;
  TGlobals.QuadRender.RenderToGBuffer(True, TTextures.QuadGBuffer);

  TGlobals.QuadRender.Clear($00000000);
  TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);
 { Position := TVec2f.Create(
    (Round(TGlobals.QuadCamera.GetPosition.X) div 256) * 256 + 128,
    (Round(TGlobals.QuadCamera.GetPosition.Y) div 256) * 256 + 128
  );
  for Y := 0 to 3 do
    for X := 0 to 4 do
      TTextures.WorldTexture[wtSnow].Ground.DrawRot(TVec2f.Create(X, Y) * 256 + Position, 0, 0.5);
                   }
  FLevelMap.Draw;

  TCustomBullet.DrawList;

  TTextures.WorldTexture[wtSnow].Rock3.DrawRot(ActionPlayer.Position, 0, 1);

  for i := 0 to TCustomObject.ObjectList.Count - 1 do
    TCustomObject.ObjectList[i].Draw;

  for i := 0 to FUnitsList.Count - 1 do
    FUnitsList[i].Draw;
   {
  for i := 0 to 3 do
    if Assigned(FSquad[i]) then
      FSquad[i].Draw;
        }

  TGlobals.QuadCamera.Disable;
  TGlobals.QuadRender.RenderToGBuffer(False);

  TTextures.QuadGBuffer.DiffuseMap.Draw(TVec2f.Zero, $FF060606);

  TGlobals.QuadCamera.Enable;
  DrawEffects;
  TGlobals.QuadCamera.Disable;

  TGlobals.QuadRender.SetBlendMode(qbmAdd);
  for i := 0 to TGlobals.LevelHeroes.Count - 1 do
    for j := 0 to TGlobals.LevelHeroes[i].Inventory - 1 do
      if Assigned(TGlobals.LevelHeroes[i].Subjects[j]) and (TGlobals.LevelHeroes[i].Subjects[j].SubjectType = stFlashLight) and TGlobals.LevelHeroes[i].Subjects[j].IsAction then
        TTextures.QuadGBuffer.DrawLight(TVec3f.Create(
          (TGlobals.LevelHeroes[i].Position.X - TGlobals.QuadCamera.GetPosition.X) / TGlobals.GWidth,
          (TGlobals.LevelHeroes[i].Position.Y - TGlobals.QuadCamera.GetPosition.Y) / TGlobals.GHeight,
          0.05
        ), 1.3, $FFFFEEDD);

  DrawEffectsLight;

  for i := 0 to TCustomObject.ObjectList.Count - 1 do
    TCustomObject.ObjectList[i].DrawLight;

  {$REGION 'Shadowscast'}

  TGlobals.QuadRender.RenderToTexture(True, TTextures.ShadowMap);
  TGlobals.QuadRender.Clear(0);
  TGlobals.QuadRender.RenderToTexture(False);

  for j := 0 to 3 do
    if Assigned(FSquad[j]) and not FSquad[j].IsNeedToKill then
    begin
      FLevelMap.DrawLightsBarriers(FSquad[j].Position);

      TGlobals.QuadRender.SetBlendMode(qbmNone);

      TGlobals.QuadRender.RenderToTexture(True, TTextures.LightsBarriersRT1, 0, 0, True);
      TShaders.LightTrace.pass1.SetShaderState(True);
      TTextures.LightsBarriers.Draw(TVec2f.Zero);
      TShaders.LightTrace.pass1.SetShaderState(False);
      TGlobals.QuadRender.RenderToTexture(False);

      TGlobals.QuadRender.RenderToTexture(True, TTextures.LightsBarriers, 0, 0, True);
      TShaders.LightTrace.pass2.SetShaderState(True);
      TTextures.LightsBarriersRT1.Draw(TVec2f.Zero);
      TShaders.LightTrace.pass2.SetShaderState(False);
      TGlobals.QuadRender.RenderToTexture(False);

      TGlobals.QuadRender.SetTextureFiltering(qtfNone);
      TShaders.LightTrace.pass3.BindVariableToPS(5, @size, 1);

      size.x := 1 / TGlobals.LightSize;
      TGlobals.QuadRender.RenderToTexture(True, TTextures.LightsBarriersDS[0]);
      TShaders.LightTrace.pass3.SetShaderState(True);
      TTextures.LightsBarriers.DrawMap(TVec2f.Zero, TVec2f.Create(TGlobals.GWidth, TGlobals.GHeight), TVec2f.Zero, TVec2f.Create(1, 1));
      TShaders.LightTrace.pass3.SetShaderState(False);
      TGlobals.QuadRender.RenderToTexture(False);

      for i := 1 to 7 do
      begin
        size.x := 1 / TTextures.LightsBarriersDS[i - 1].GetTextureWidth;
        TGlobals.QuadRender.RenderToTexture(True, TTextures.LightsBarriersDS[i]);

        TShaders.LightTrace.pass3.SetShaderState(True);
        TTextures.LightsBarriersDS[i - 1].DrawMap(TVec2f.Zero, TVec2f.Create(TGlobals.GWidth, TGlobals.GHeight), TVec2f.Zero, TVec2f.Create(1, 1));
        TShaders.LightTrace.pass3.SetShaderState(False);
        TGlobals.QuadRender.RenderToTexture(False);
      end;

      TGlobals.QuadRender.RenderToTexture(True, TTextures.LightsBarriers, 0, 0, True);
      TGlobals.QuadRender.SetBlendMode(qbmNone);
      TShaders.LightTrace.pass4.SetShaderState(True);
      TTextures.LightsBarriersDS[7].DrawMap(TVec2f.Zero, TVec2f.Create(TGlobals.LightSize, TGlobals.LightSize), TVec2f.Zero, TVec2f.Create(1, 1));
      TShaders.LightTrace.pass4.SetShaderState(False);
      TGlobals.QuadRender.RenderToTexture(False);

      TGlobals.QuadRender.RenderToTexture(True, TTextures.LightsBarriersRT1, 0, 0, True);
      TShaders.LightTrace.pass5.SetShaderState(True);
      TTextures.LightsBarriers.Draw(TVec2f.Zero);
      TShaders.LightTrace.pass5.SetShaderState(False);
      TGlobals.QuadRender.RenderToTexture(False);

      TGlobals.QuadRender.RenderToTexture(True, TTextures.ShadowMap);
      TGlobals.QuadRender.SetBlendMode(qbmAdd);
      TShaders.LightTrace.pass6.SetShaderState(True);
      TTextures.LightsBarriersRT1.Draw(FSquad[j].Position - TGlobals.QuadCamera.GetPosition - TVec2f.Create(TGlobals.LightSize / 2, TGlobals.LightSize / 2));
      TShaders.LightTrace.pass6.SetShaderState(False);
      TGlobals.QuadRender.RenderToTexture(False);

      TGlobals.QuadRender.FlushBuffer;
      TGlobals.QuadRender.SetTextureFiltering(qtfLinear);
    end;

  TGlobals.QuadRender.SetBlendMode(qbmMul);
  TTextures.ShadowMap.Draw(TVec2f.Zero);
  {$ENDREGION}
  TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);
  if Assigned(FLasso) then
  begin
    TGlobals.QuadCamera.Enable;
    if FLasso.Position.Distance(FMousePosition) > FLassoWidth then
      TGlobals.QuadRender.DrawQuadLine(FLasso.Position, FLasso.Position + (FMousePosition - FLasso.Position).Normalize * FLassoWidth, 1, 1, $FF00FF00, $FF00FF00)
    else
      TGlobals.QuadRender.DrawQuadLine(FLasso.Position, FMousePosition, 1, 1, $FF00FF00, $FF00FF00);
    TGlobals.QuadCamera.Disable;
  end;
  LeftPanelDraw;
  TGlobals.EarthFont.TextOut(TVec2f.Create(6, 200), 0.5, PWideChar(IntToStr(TGlobals.LevelHeroes.Count)));
end;

procedure TGameModePlanet.Collides;
  procedure PushOut(AUnit: TCustomUnit; const APoint: TVec2f);
  var
    temp: TVec2f;
  begin
    temp := AUnit.Position - APoint;
    AUnit.Move(temp.Normalize * (AUnit.Radius - temp.Length));
  end;

  procedure CollideCircle(AUnit: TCustomUnit; const ACenter: TVec2f; const ARadius: Single);
  begin
    if AUnit.Position.Distance(ACenter) < AUnit.Radius + ARadius then
      PushOut(AUnit, (AUnit.Position - ACenter).Normalize * ARadius + ACenter);
  end;

  function LinesCollPoint(const A, B, C, D: TVec2f): TVec2f;
  var
    t, tmp: Single;
    PointA, PointB: TVec2f;
  begin
    PointA := B - A;
    PointB := C - D;

    tmp := 1 / (PointB.X * (-PointA.Y) - (-PointA.X) * PointB.Y);
    t := (PointB.X * (A.Y - C.Y) - (A.X - C.X) * PointB.Y) * tmp;

    Result.X := A.X + t * PointA.X;
    Result.Y := A.Y + t * PointA.Y;
  end;

  procedure CollideLine(const AUnit: TCustomUnit; const APoint, BPoint: TVec2f);
  var
    A, B, C: Single;
    Vector: TVec2f;
    PointA, PointB, PointC, PointD: TVec2f;
  begin
    PointA := APoint - AUnit.Position;
    PointB := BPoint - AUnit.Position;

    Vector.X := PointB.X - PointA.X;
    Vector.Y := PointB.Y - PointA.Y;

    A := sqr(Vector.X) + sqr(Vector.Y);
    B := (PointA.X * Vector.X + PointA.Y * Vector.Y) * 2;
    C := sqr(PointA.X) + sqr(PointA.Y) - sqr(AUnit.Radius);

    if B <= 0 then
    begin
      if -B < A * 2 then
      begin
        if A * C * 4 - sqr(B) < 0 then
        begin
          Vector := (PointB - PointA).Normal.Normalize * AUnit.Radius;
          PointC := Vector;
          PointD := -Vector;

          PushOut(AUnit, LinesCollPoint(PointA, PointB, PointC, PointD) + AUnit.Position);
        end;
      end
      else if A + B + C < 0 then
        PushOut(AUnit, BPoint);
    end
    else if C < 0 then
      PushOut(AUnit, APoint);
  end;

  procedure ColledeUnit(AUnit: TCustomUnit);
  var
    X, Y: Integer;
    Range: TMapRange;
    Position: TVec2f;
    Subject: TCustomSubject;
  begin
    Range := FLevelMap.GetMapRange(
      AUnit.Position - TVec2f.Create(40, 40),
      AUnit.Position + TVec2f.Create(40, 40)
    );
    for Y := Range.A.Y to Range.B.Y do
      for X := Range.A.X to Range.B.X do
      begin
        Position := TVec2f.Create(X, Y) * FLevelMap.FieldSize;
        if FLevelMap.Fields[X, Y].FieldType <> ftNone then
          case FLevelMap.Fields[X, Y].FieldType of
            ftStone: CollideCircle(AUnit, Position + TVec2f.Create(FLevelMap.FieldSize, FLevelMap.FieldSize) / 2, FLevelMap.FieldSize / 2);
            ftWall, ftGlass:
              begin
                CollideLine(AUnit, Position, Position + TVec2f.Create(FLevelMap.FieldSize, 0));
                CollideLine(AUnit, Position + TVec2f.Create(FLevelMap.FieldSize, 0), Position + TVec2f.Create(FLevelMap.FieldSize, FLevelMap.FieldSize));
                CollideLine(AUnit, Position + TVec2f.Create(FLevelMap.FieldSize, FLevelMap.FieldSize), Position + TVec2f.Create(0, FLevelMap.FieldSize));
                CollideLine(AUnit, Position + TVec2f.Create(0, FLevelMap.FieldSize), Position);
              end;
          end;
        if (FLevelMap.Fields[X, Y].Obj is TCustomSubject) then
        begin
          Subject := TCustomSubject(FLevelMap.Fields[X, Y].Obj);
          if not Subject.ParentAction then
            CollideCircle(AUnit, Position + TVec2f.Create(FLevelMap.FieldSize, FLevelMap.FieldSize) / 2, Subject.Radius);
        end;
      end;
  end;
var
  i, j, X, Y: Integer;
  Line: T2DLine;
  Dis, DisM: Single;
  Range: TMapRange;
  CollPoint: TVec2f;
  ColPoints: T2DColPoint;
  Position: TVec2f;
begin
  for i := 0 to FUnitsList.Count - 1 do
    ColledeUnit(FUnitsList[i]);

  for i := TCustomBullet.BulletList.Count - 1 downto 0 do
  begin
    Line.a := TCustomBullet.BulletList[i].OldPosition;
    Line.b := TCustomBullet.BulletList[i].Position;
    Range := FLevelMap.GetMapRange(Line.a, Line.b);
    ColPoints.Count := 0;
    for Y := Range.A.Y to Range.B.Y do
      for X := Range.A.X to Range.B.X do
      begin
        Position := TVec2f.Create(X, Y) * FLevelMap.FieldSize;
        case LevelMap.Fields[X, Y].FieldType of
          ftWall, ftGlass: TCollision.LineRect(Line, T2DRect.Create(Position, TVec2f.Create(1, 1) * FLevelMap.FieldSize), @ColPoints);
          ftStone: TCollision.LineRect(Line, T2DRect.Create(Position, TVec2f.Create(0.5, 0.5) * FLevelMap.FieldSize), @ColPoints);
          //ftStone: TCollision.LineCircle(Line, T2DCircle.Create(Position, FLevelMap.FieldSize * 0.66),  )
        end;
      end;

    if ColPoints.Count > 0 then
    begin
      CollPoint := ColPoints.Points[0];
      DisM := CollPoint.Distance(Line.a);
      for j := 1 to ColPoints.Count - 1 do
      begin
        Dis := ColPoints.Points[j].Distance(Line.a);
        if Dis < DisM then
        begin
          CollPoint := ColPoints.Points[j];
          DisM := Dis;
        end;
      end;
      TCustomBullet.BulletList[i].Coll(CollPoint, TVec2f.Zero);
    end;
  end;
end;

function TGameModePlanet.GetMousePositionToCamera: TVec2f;
var
  sinA, cosA: Single;
  Position: TVec2f;
  ScreenSize: TVec2f;
begin
  ScreenSize := TVec2f.Create(TGlobals.GWidth, TGlobals.GHeight);
  SinCos(DegToRad(-TGlobals.QuadCamera.GetAngle), sinA, cosA);

  Position := TGlobals.QuadInput.GetMousePosition - ScreenSize / 2;
  Result.X := Position.X * cosA - Position.Y * sinA;
  Result.Y := Position.X * sinA + Position.Y * cosA;
  Result := Result + ScreenSize / 2 + TGlobals.QuadCamera.GetPosition;
end;

end.
