unit Pong;

interface

uses
  Classes, SysUtils, Game, Scene, Graphics, DateUtils;

type

  { TSceneMain }

  TSceneMain = class(TScene)
    Game: TGame;
    procedure Draw; override;
  end;

  TPlayer = record
    Score: Integer;
    PaddlePos: Integer;
    Rect: TRect;
  end;

  { TPong }

  TPong = class(TGame)
  private
    Step: TPoint;
    BallPos: TPoint;
    PlayerLeft: TPlayer;
    PlayerRight: TPlayer;
    procedure InitBall;
  public
    Width: Integer;
    Height: Integer;
    Min: Integer;
    Max: Integer;
    Winner: string;
    BallMoveTime: TDateTime;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure Tick; override;
    procedure Run; override;
    constructor Create; override;
    destructor Destroy; override;
  end;


implementation

resourcestring
  SPong = 'Pong';
  SWinnerIs = 'The winner is';

{ TSceneMain }

procedure TSceneMain.Draw;
var
  Shift: Integer;
  Center: Integer;
  PlateHeight: Integer;
  PongGame: TPong;
begin
  BackgroundColor := clBlack;
  Clear;
  PongGame := TPong(Game);
  with Canvas do begin
    PongGame.Width := Width;
    PongGame.Height := Height;

    Brush.Color := clGray;
    Center := Height div 2;
    PlateHeight := Height div 3;

    Shift := PongGame.PlayerLeft.PaddlePos * Height div 35;
    PongGame.PlayerLeft.Rect := Rect(20, Center + Shift - PlateHeight div 2,
      20 + 20, Center + Shift + PlateHeight div 2);
    FillRect(PongGame.PlayerLeft.Rect);

    Shift := PongGame.PlayerRight.PaddlePos * Height div 35;
    PongGame.PlayerRight.Rect := Rect(Width - 20 - 20, Center + Shift - PlateHeight div 2,
      Width - 20, Center + Shift + PlateHeight div 2);
    FillRect(PongGame.PlayerRight.Rect);

    Ellipse(PongGame.BallPos.X - 10, PongGame.BallPos.Y - 10,
      PongGame.BallPos.X + 10, PongGame.BallPos.Y + 10);

    Brush.Style := bsClear;
    TextOut(Width div 4, 100, IntToStr(PongGame.PlayerLeft.Score));
    TextOut((Width div 4) * 3, 100, IntToStr(PongGame.PlayerRight.Score));

    Pen.Style := psDash;
    Line(Width div 2, 0, Width div 2, Height);

    if PongGame.Winner <> '' then begin
      TextOut(100, 200, SWinnerIs + ' ' + PongGame.Winner);
    end;
  end;
end;

{ TPong }

procedure TPong.InitBall;
begin
  BallPos := Point(0, 0);
  Step := Point(10, 10);
end;

procedure TPong.KeyUp(var Key: Word; Shift: TShiftState);
begin
  if not (State = gsRunning) then Exit;
  case Key of
    KeyCodeDown: begin
      if PlayerLeft.PaddlePos < Max then Inc(PlayerLeft.PaddlePos);
      Scene.Redraw;
    end;
    KeyCodeUp: begin
      if PlayerLeft.PaddlePos > Min then Dec(PlayerLeft.PaddlePos);
      Scene.Redraw;
    end;
    KeyCodeLeft: begin
      if PlayerRight.PaddlePos < Max then Inc(PlayerRight.PaddlePos);
      Scene.Redraw;
    end;
    KeyCodeRight: begin
      if PlayerRight.PaddlePos > Min then Dec(PlayerRight.PaddlePos);
      Scene.Redraw;
    end;
  end;
end;

procedure TPong.Tick;
begin
  if Time > BallMoveTime + 50 * OneMillisecond then begin
    BallMoveTime := Time;

    BallPos.X := BallPos.X + Step.X;
    BallPos.Y := BallPos.Y + Step.Y;
    if BallPos.Y < 0 then begin
      Step.Y := -Step.Y;
      BallPos.Y := BallPos.Y + 2 * Step.Y;
    end;
    if BallPos.Y > Height then begin
      Step.Y := -Step.Y;
      BallPos.Y := BallPos.Y + 2 * Step.Y;
    end;
    if BallPos.X > Width then begin
      PlayerLeft.Score := PlayerLeft.Score + 1;
      InitBall;
    end;
    if BallPos.X < 0 then begin
      PlayerRight.Score := PlayerRight.Score + 1;
      InitBall;
    end;
    if PlayerLeft.Rect.Contains(BallPos) then begin
      Step.X := -Step.X;
      BallPos.X := BallPos.X + 2 * Step.X;
    end;
    if PlayerRight.Rect.Contains(BallPos) then begin
      Step.X := -Step.X;
      BallPos.X := BallPos.X + 2 * Step.X;
    end;
    Scene.Redraw;
  end;
end;

procedure TPong.Run;
begin
  Min := -10;
  Max := 10;
  PlayerLeft.PaddlePos := 0;
  PlayerLeft.Score := 0;
  PlayerRight.PaddlePos := 0;
  PlayerRight.Score := 0;
  Winner := '';
  InitBall;
  inherited;
end;

constructor TPong.Create;
begin
  Name := SPong;
  Scene := TSceneMain.Create;
  TSceneMain(Scene).Game := Self;
end;

destructor TPong.Destroy;
begin
  FreeAndNil(Scene);
  inherited;
end;

end.

