{************************************************}
{                                                }
{   Breakout Demo Program                        }
{   Copyright (c) 1992 by Borland International  }
{                                                }
{************************************************}

unit Walls;

{
  See BREAKOUT.PAS.
  This unit defines the Wall object type.
  It's a fairly complex object, because it plays such a
  pivotal role in the game.
}

interface

uses Screen, Bricks, Bounds, Crt;

type
  BrickPtr = ^Brick;
  BW = array[1..1000] of Brick;
  WallPtr = ^BW;

  Wall = object(Obstacle)
    BrickWall : WallPtr;
    Height : Integer;
    NumLeft : Integer;
    Value : Integer;
    NCells : Integer;
    constructor Init(InitX, InitY, InitWidth, InitHeight : Integer);
    destructor Done; virtual;
    procedure Show; virtual;
    procedure Hide; virtual;
    function Collide(var B : Ball) : Boolean; virtual;
    function GetValue : Integer; virtual;
    procedure Reset;
  end;

implementation

function RandomColor(MaxColors : Integer) : Integer;
var
  C : Integer;
begin
  C := Random(MaxColors);
  while C = (TextAttr SHR 4) do
    C := Random(MaxColors);
  RandomColor := C;
end;

procedure Beep;
begin
  Sound(100);
  Delay(20);
  NoSound;
end;

{ A wall is an array of bricks.  Its constructor actually builds a
  conformant array, so we don't have to hardcode the size of the
  wall. }

constructor Wall.Init(InitX, InitY, InitWidth, InitHeight : Integer);
begin
  Obstacle.Init(InitX, InitY, InitWidth, False);
  Height := InitHeight;
  NCells := Width*5;
  GetMem(BrickWall, Width*Height*SizeOf(Brick));
  Reset;
end;

destructor Wall.Done;
begin
  FreeMem(BrickWall, Width*Height*SizeOf(Block));
end;

{ This procedure could be made simpler, but you wouldn't get the slick
  effect you see when the wall is built. }

procedure Wall.Show;
var
  CurCol : Integer;
  Count : Integer;
  CurBlock : Integer;
begin
  Visible := True;
  NumLeft := Width*Height;
  for CurCol := 1 to Width + Height - 1 do
    for Count := 0 to Height - 1 do
    begin
      CurBlock := CurCol + Count*(Width-1);
      if (CurCol - Count >= 1) and (CurCol - Count <= Width) then
      begin
        BrickWall^[CurBlock].Show;
        Delay(5);
      end;
    end;
  GoToXY(X + (5*Width DIV 2) - 7, Y);
  TextColor(WHITE);
  Write('Turbo Breakout');
end;

procedure Wall.Hide;
var
  CurCol : Integer;
  Count : Integer;
  CurBlock : Integer;
begin
  Visible := False;
  for CurCol := 1 to Width + Height - 1 do
    for Count := 0 to Height - 1 do
    begin
      CurBlock := CurCol + Count*(Width-1);
      if (CurCol - Count >= 1) and (CurCol - Count <= Width) then
      begin
        if BrickWall^[CurBlock].IsVisible then
        begin
          BrickWall^[CurBlock].Hide;
          Delay(5);
        end;
      end;
    end;
end;

function Wall.Collide(var B : Ball) : Boolean;
var
  CollideV, CollideH : Boolean;

{ To check for a collision with a brick, first we check if the ball is in
  the area where the wall is located, then we see if there's a brick that's
  still visible at the ball's position.  If so, we destroy the brick, grab
  its value, and beep. }

function CheckCollide(XPos, YPos : Integer) : Boolean;
var
  ThisBrick : BrickPtr;
begin
  CheckCollide := False;
  if (YPos < Y) or (YPos > Y + Height - 1) or
     (XPos < X) or (XPos > X + NCells - 1) then
    Exit;
  ThisBrick := @BrickWall^[1 + ((XPos-1) DIV 5) + Width*(YPos - 1)];
  if ThisBrick^.IsVisible then
  begin
    CheckCollide := True;
    Inc(Value, ThisBrick^.GetValue);
    ThisBrick^.Hide;
    Dec(NumLeft);
    Beep;
    if NumLeft = 0 then
      Show;
  end
end;

{ When checking for a collision with the wall, we have to watch out
  for special cases involving corners. }

begin
  Collide := False;
  Value := 0;
  CollideV := CheckCollide(B.X, B.NextY);
  CollideH := CheckCollide(B.NextX, B.Y);
  if CollideV then
  begin
    Collide := True;
    B.ReverseY;
  end;
  if CollideH then
  begin
    Collide := True;
    B.ReverseX;
  end;
  if not CollideV and not CollideH then
    if CheckCollide(B.NextX, B.NextY) then
    begin
      Collide := True;
      B.ReverseX;
      B.ReverseY;
    end;
end;

function Wall.GetValue : Integer;
begin
    GetValue := Value;
end;

procedure Wall.Reset;
var
  CurRow : Integer;
  CurCol : Integer;
  MaxColors : Integer;
begin
  if LastMode = Mono then
    MaxColors := 4
  else
    MaxColors := 16;
  NumLeft := Width*Height;
  for CurRow := 0 to Height - 1 do
    for CurCol := 0 to Width - 1 do
      BrickWall^[CurRow*Width+CurCol+1].Init(X + CurCol*5,
                                             Y + CurRow,
                                             RandomColor(MaxColors),
                                             Height - Y - CurRow + 1);
  if Visible then
    Show;
end;

end.
