unit u_body;

interface

uses zglHeader, u_map, u_const, u_portal;

type
  TBody = class(TObject)
    public
      fLastTP: single;
      fMass: single;
      fLastPortal: TPortal;
      fMap: TMap;
      fSpeed: zglTPoint2D;
      fCollide: Boolean;
      fGround: single;
      fPosition: zglTRect;
      fAngle: Single;

      property Position: zglTRect read fPosition;
      procedure ApplyImpulse(X, Y: Single);
      procedure Draw; virtual;
      procedure Update(dt: Double); virtual;
      constructor Create(pMap: TMap; X, Y, W, H, spX, spY, pMass: Single);
      destructor Destroy; override;
  end;

implementation

uses u_game;

constructor TBody.Create;
begin
  fPosition.X := X; fPosition.Y := Y;
  fPosition.W := W; fPosition.H := H;
  fSpeed.X := spX; fSpeed.Y := spY;
  fMap := pMap;
  fMass := pMass;
  fLastTP := 0;
  fLastPortal := nil;
  fCollide := false; fGround := 0;
  fMap.fBodies.Add(Self);
end;

procedure TBody.Draw;
begin
  //
end;

procedure TBody.Update(dt: Double);
var bx, by, ex, ey, i, j, a, pX, pY: integer;
    pAngle, pDist, pOffset: single;
    ln: array[0..3] of zglTLine;
    bl: TBlock;
    lnp: zglTLine;
    dtr: zglTRect;
    col: zglTPoint2D;
    b: boolean;
    p, e: TPortal;
begin

  if fLastTP > 0 then
    fLastTP := fLastTP - dt;

  fSpeed.Y := fSpeed.Y + fMass * PHYS_GRAVITATION * dt;

  bx := round(Max(fPosition.X - 2, 0));
  by := round(Max(fPosition.Y - 2, 0));
  ex := round(Min(fPosition.X + 2, fMap.Width - 1));
  ey := round(Min(fPosition.Y + 2, fMap.Height - 1));

  dtr.W := 1; dtr.H := 1;

  b := true;

  fPosition.X := fPosition.X + fSpeed.X * dt;
  fPosition.Y := fPosition.Y + fSpeed.Y * dt;

  if fLastTP <= 0 then
    for i := 0 to fMap.fPortals.Count - 1 do begin
      p := fMap.fPortals.Items[i];
      if p.fExit <> nil then begin
        lnp.x0 := p.fX + m_Cos(p.fAngle * 90 - 90) * 0.5;
        lnp.y0 := p.fY + m_Sin(p.fAngle * 90 - 90) * 0.5;
        lnp.x1 := p.fX + m_Cos(p.fAngle * 90 + 90) * 0.5;
        lnp.y1 := p.fY + m_Sin(p.fAngle * 90 + 90) * 0.5;
        if fLastPortal <> p then
          if col2d_LineVsRect(lnp, fPosition, @col) then begin
            fLastTP := 50;
            e := p.fExit;
            // расстояние от игрока до портала
            pOffset := m_Distance(fPosition.X + fPosition.W / 2,
                                  fPosition.Y + fPosition.H / 2,
                                  lnp.x1, lnp.y1);
            // меняем координаты игрока
            fPosition.X :=
              e.fX + m_Cos(e.fAngle * 90) * fPosition.W / 2
                {+ m_Cos(e.fAngle * 90 + 90) * (pOffset - 0.5)} - fPosition.W / 2;
            fPosition.Y :=
              e.fY + m_Sin(e.fAngle * 90) * fPosition.H / 2
                {+ m_Sin(e.fAngle * 90 + 90) * (pOffset - 0.5)} - fPosition.W / 2;
            // и угол
            fAngle := e.fAngle;
            // пересчитывем импульс
            pDist := m_Distance(0, 0, fSpeed.X, fSpeed.Y);
            pAngle := m_Angle(0, 0, fSpeed.X, fSpeed.Y);
            // отнимаеи углы
            pAngle := pAngle + ((e.fAngle -
              p.fAngle) * 90);
            // обновляем импульс
            fSpeed.X := Cos(pAngle * deg2rad) * pDist;
            fSpeed.Y := Sin(pAngle * deg2rad) * pDist;
            // если надо - обращаем
            if (p.fAngle = e.fAngle) then begin
              case p.fAngle of
                0, 2: begin
                  fSpeed.Y := -fSpeed.Y;
                end;
                else begin
                  fSpeed.X := -fSpeed.X;
                end;
              end;
            end;
            fLastPortal := e;
            snd_Play(blEffects[p.fClassOf].sndExit);
          end;
      end;
    end;

  if b then begin
    fLastPortal := nil;
    with fMap do
    for j := by to ey do begin
      for i := bx to ex do begin
        if fMap.fData[j][i].Wall then begin
          dtr.X := i; dtr.Y := j;
          //if col2d_Rect(fPosition, dtr) then begin

            ln[0].x0 := i + V_MIN; ln[0].x1 := i + V_MAX;
            ln[0].y0 := j; ln[0].y1 := j;

            ln[1].x0 := i + V_MIN; ln[1].x1 := i + V_MAX;
            ln[1].y0 := j + 1; ln[1].y1 := j + 1;

            ln[2].x0 := i + 1; ln[2].x1 := i + 1;
            ln[2].y0 := j + V_MIN; ln[2].y1 := j + V_MAX;

            ln[3].x0 := i; ln[3].x1 := i;
            ln[3].y0 := j + V_MIN; ln[3].y1 := j + V_MAX;

            if col2d_LineVsRect(Ln[0], fPosition, nil) and
              (fData[j][i].Data and 8 > 0) then begin
              // сверху
              fGround := fGround + 0.1 * dt;
              fPosition.Y := dtr.Y - fPosition.H;
              fSpeed.Y := min(fSpeed.Y, 0);
            end;

            if col2d_LineVsRect(Ln[1], fPosition, nil) and
              (fData[j][i].Data and 2 > 0) then begin
              // снизу
              fPosition.Y := dtr.Y + dtr.H;
              fSpeed.Y := max(fSpeed.Y, 0);
            end;

            if col2d_LineVsRect(Ln[2], fPosition, nil) and
              (fData[j][i].Data and 1 > 0) then begin
              // справа
              fPosition.X := dtr.X + dtr.W;
              fSpeed.X := max(fSpeed.X, 0);
            end;
            if col2d_LineVsRect(Ln[3], fPosition, nil) and
              (fData[j][i].Data and 4 > 0) then begin
              // слева
              fPosition.X := dtr.X - fPosition.W;
              fSpeed.X := min(fSpeed.X, 0);
            end;
          //end;
        end;
      end;
    end;
  end;

  if (fPosition.X < 0) and (fSpeed.X < 0) then begin
    fPosition.X := 0;
    fSpeed.X := 0;
  end;
  if (fPosition.Y < 0) and (fSpeed.Y < 0) then begin
    fPosition.Y := 0;
    fSpeed.Y := 0;
  end;
  if (fPosition.X + fPosition.W > fMap.Width - 1) and (fSpeed.X > 0) then begin
    fPosition.X := fMap.Width - fPosition.W - 1;
    fSpeed.X := 0;
  end;
  if (fPosition.Y + fPosition.H > fMap.Height - 1) and (fSpeed.Y > 0) then begin
    fPosition.Y := fMap.Height - fPosition.H - 1;
    fSpeed.Y := 0;
  end;


  {if fCollide and (fGround > 2) then begin
    fSpeed.X := fSpeed.X / SPEED_DUMPING;
  end else begin
  end;  }
  fSpeed.X := fSpeed.X / SPEED_DUMPINGX;
  fSpeed.Y := fSpeed.Y / SPEED_DUMPINGY;


  pX := Round(fPosition.X);
  pY := Round(fPosition.Y);
  bl := fMap.fData[pY, pX].Block;
  case bl of
    blButton: begin
      fMap.fData[pY, pX].Time := BLOCK_TIME;
      fMap.fData[pY, pX].Tile := 2;
    end;
  end;

end;

procedure TBody.ApplyImpulse(X, Y: Single);
begin
  fSpeed.X := fSpeed.X + X;
  fSpeed.Y := fSpeed.Y + Y;
end;

destructor TBody.Destroy;
begin
  inherited;
end;

end.
