329 lines
7.9 KiB
Plaintext
329 lines
7.9 KiB
Plaintext
|
|
|||
|
{ Copyright (c) 1989 by Borland International, Inc. }
|
|||
|
|
|||
|
unit Bounds;
|
|||
|
{ Turbo Pascal 5.5 object-oriented example.
|
|||
|
See BREAKOUT.PAS.
|
|||
|
Contains the Paddle object type and the object types that
|
|||
|
define the boundaries of the playfield.
|
|||
|
This unit is part of the BREAKOUT.PAS example.
|
|||
|
}
|
|||
|
|
|||
|
interface
|
|||
|
|
|||
|
uses Screen, Bricks, Count, Crt;
|
|||
|
|
|||
|
type
|
|||
|
ObstaclePtr = ^Obstacle;
|
|||
|
|
|||
|
{ An ObstacleList is a list of instances of objects derived from the
|
|||
|
object Obstacle. In order to use all these instances polymorphically,
|
|||
|
All their virtual functions have to have corresponding virtual functions
|
|||
|
in Obstacle, even if they are never used. }
|
|||
|
|
|||
|
Obstacle = object(Location)
|
|||
|
Width : Integer;
|
|||
|
Trap : Boolean;
|
|||
|
NextPtr : ObstaclePtr;
|
|||
|
constructor Init(InitX, InitY, InitWidth : Integer; SetTrap : Boolean);
|
|||
|
destructor Done; virtual;
|
|||
|
function Collide(var B : Ball) : Boolean; virtual;
|
|||
|
function IsTrap : Boolean; virtual;
|
|||
|
function GetValue : Integer; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
ObstacleList = object
|
|||
|
Head : Obstacle;
|
|||
|
Tail : ObstaclePtr;
|
|||
|
constructor Init;
|
|||
|
destructor Done; virtual;
|
|||
|
procedure Append(NewObstacle : ObstaclePtr);
|
|||
|
procedure Show;
|
|||
|
procedure Hide;
|
|||
|
function CheckCollisions(var B : Ball; var Score : Counter) : Boolean;
|
|||
|
end;
|
|||
|
|
|||
|
Paddle = object(Obstacle)
|
|||
|
Color : Integer;
|
|||
|
constructor Init(InitX, InitY, InitColor : Integer);
|
|||
|
destructor Done; virtual;
|
|||
|
procedure Show; virtual;
|
|||
|
procedure Hide; virtual;
|
|||
|
procedure MoveTo(NewX, NewY : Integer); virtual;
|
|||
|
function Collide(var B : Ball) : Boolean; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
{ There are no instances of the object Boundary. It's here to provide
|
|||
|
a common basis for the next four objects. }
|
|||
|
Boundary = object(Obstacle)
|
|||
|
constructor Init(InitX, InitY, InitWidth : Integer; SetTrap : Boolean);
|
|||
|
end;
|
|||
|
|
|||
|
LeftBound = object(Boundary)
|
|||
|
constructor Init(InitX, InitY, InitWidth : Integer; SetTrap : Boolean);
|
|||
|
function Collide(var B : Ball) : Boolean; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
UpperBound = object(Boundary)
|
|||
|
constructor Init(InitX, InitY, InitWidth : Integer; SetTrap : Boolean);
|
|||
|
function Collide(var B : Ball) : Boolean; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
RightBound = object(Boundary)
|
|||
|
constructor Init(InitX, InitY, InitWidth : Integer; SetTrap : Boolean);
|
|||
|
function Collide(var B : Ball) : Boolean; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
LowerBound = object(Boundary)
|
|||
|
constructor Init(InitX, InitY, InitWidth : Integer; SetTrap : Boolean);
|
|||
|
function Collide(var B : Ball) : Boolean; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
implementation
|
|||
|
|
|||
|
constructor Obstacle.Init(InitX, InitY, InitWidth : Integer;
|
|||
|
SetTrap : Boolean);
|
|||
|
begin
|
|||
|
Location.Init(InitX, InitY);
|
|||
|
Width := InitWidth;
|
|||
|
Trap := SetTrap;
|
|||
|
NextPtr := nil;
|
|||
|
end;
|
|||
|
|
|||
|
destructor Obstacle.Done;
|
|||
|
begin
|
|||
|
end;
|
|||
|
|
|||
|
function Obstacle.Collide(var B : Ball) : Boolean;
|
|||
|
begin
|
|||
|
Collide := True;
|
|||
|
end;
|
|||
|
|
|||
|
function Obstacle.IsTrap : Boolean;
|
|||
|
begin
|
|||
|
IsTrap := Trap;
|
|||
|
end;
|
|||
|
|
|||
|
function Obstacle.GetValue : Integer;
|
|||
|
begin
|
|||
|
GetValue := 0;
|
|||
|
end;
|
|||
|
|
|||
|
constructor ObstacleList.Init;
|
|||
|
begin
|
|||
|
Head.Init(0, 0, 0, False);
|
|||
|
Tail := @Head;
|
|||
|
end;
|
|||
|
|
|||
|
destructor ObstacleList.Done;
|
|||
|
var
|
|||
|
Temp1, Temp2 : ObstaclePtr;
|
|||
|
begin
|
|||
|
Temp1 := Head.NextPtr;
|
|||
|
while Temp1 <> nil do
|
|||
|
begin
|
|||
|
Temp2 := Temp1;
|
|||
|
Temp1 := Temp1^.NextPtr;
|
|||
|
Temp2^.Done;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
procedure ObstacleList.Append(NewObstacle : ObstaclePtr);
|
|||
|
begin
|
|||
|
Tail^.NextPtr := NewObstacle;
|
|||
|
Tail := NewObstacle;
|
|||
|
end;
|
|||
|
|
|||
|
procedure ObstacleList.Show;
|
|||
|
var
|
|||
|
Current : ObstaclePtr;
|
|||
|
begin
|
|||
|
Current := Head.NextPtr;
|
|||
|
while Current <> nil do
|
|||
|
begin
|
|||
|
Current^.Show;
|
|||
|
Current := Current^.NextPtr;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
procedure ObstacleList.Hide;
|
|||
|
var
|
|||
|
Current : ObstaclePtr;
|
|||
|
begin
|
|||
|
Current := Head.NextPtr;
|
|||
|
while Current <> nil do
|
|||
|
begin
|
|||
|
Current^.Hide;
|
|||
|
Current := Current^.NextPtr;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
|
|||
|
{ This function is a little more complex than I like. It checks
|
|||
|
whether a collision occurs, and updates the score if one does. }
|
|||
|
|
|||
|
function ObstacleList.CheckCollisions(var B : Ball;
|
|||
|
var Score : Counter) : Boolean;
|
|||
|
var
|
|||
|
Current : ObstaclePtr;
|
|||
|
begin
|
|||
|
CheckCollisions := False;
|
|||
|
Current := Head.NextPtr;
|
|||
|
while Current <> nil do
|
|||
|
begin
|
|||
|
if Current^.Collide(B) then
|
|||
|
begin
|
|||
|
Score.Add(Current^.GetValue);
|
|||
|
if Current^.IsTrap then
|
|||
|
CheckCollisions := True;
|
|||
|
end;
|
|||
|
Current := Current^.NextPtr;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
constructor Paddle.Init(InitX, InitY, InitColor : Integer);
|
|||
|
begin
|
|||
|
Obstacle.Init(InitX, InitY, 5, False);
|
|||
|
Color := InitColor;
|
|||
|
end;
|
|||
|
|
|||
|
destructor Paddle.Done;
|
|||
|
begin
|
|||
|
Obstacle.Done;
|
|||
|
end;
|
|||
|
|
|||
|
procedure Paddle.Show;
|
|||
|
var
|
|||
|
Str : String[10];
|
|||
|
begin
|
|||
|
FillChar(Str[1], Width, Chr(223));
|
|||
|
Str[0] := Chr(Width);
|
|||
|
Location.Show;
|
|||
|
TextColor(Color);
|
|||
|
GoToXY(X, Y);
|
|||
|
Write(Str);
|
|||
|
end;
|
|||
|
|
|||
|
procedure Paddle.Hide;
|
|||
|
begin
|
|||
|
Location.Hide;
|
|||
|
GoToXY(X, Y);
|
|||
|
Write('' : Width);
|
|||
|
end;
|
|||
|
|
|||
|
{ The motion of Paddle is restricted to the 80-character screen }
|
|||
|
|
|||
|
procedure Paddle.MoveTo(NewX, NewY : Integer);
|
|||
|
begin
|
|||
|
Hide;
|
|||
|
if NewX < 1 then
|
|||
|
X := 1
|
|||
|
else if NewX > 81 - Width then
|
|||
|
X := 81 - Width
|
|||
|
else
|
|||
|
X := NewX;
|
|||
|
Y := NewY;
|
|||
|
Show;
|
|||
|
end;
|
|||
|
|
|||
|
{ If the ball hits the paddle we have to change the ball's direction.
|
|||
|
Also, to keep the overall logic simpler, if the paddle is at the
|
|||
|
edge of the screen and the ball would miss the paddle and go off the
|
|||
|
edge, we call it a hit. If we don't do this here, we get into some
|
|||
|
complications with bouncing off the sides of the screen }
|
|||
|
|
|||
|
function Paddle.Collide(var B : Ball) : Boolean;
|
|||
|
var
|
|||
|
NewX, NewY : Integer;
|
|||
|
begin
|
|||
|
NewX := B.NextX;
|
|||
|
NewY := B.NextY;
|
|||
|
Collide := False;
|
|||
|
if (NewY = Y) then
|
|||
|
if ((NewX >= X) and (NewX < X + Width)) or
|
|||
|
((NewX < 1) and (X = 1)) or
|
|||
|
((NewX > 80) and (X + Width = 81)) then
|
|||
|
begin
|
|||
|
B.ReverseY;
|
|||
|
{$IFDEF Test} { If the paddle is following the ball, we have to put
|
|||
|
in some random behavior so it doesn't get boring. }
|
|||
|
B.ChangeXVel(Integer(Random(2))*2-1);
|
|||
|
{$ELSE}
|
|||
|
B.ChangeXVel(B.GetX - X - 2);
|
|||
|
{$ENDIF}
|
|||
|
Collide := True;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
constructor Boundary.Init(InitX, InitY, InitWidth : Integer;
|
|||
|
SetTrap : Boolean);
|
|||
|
begin
|
|||
|
Obstacle.Init(InitX, InitY, InitWidth, SetTrap);
|
|||
|
end;
|
|||
|
|
|||
|
constructor LeftBound.Init(InitX, InitY, InitWidth : Integer;
|
|||
|
SetTrap : Boolean);
|
|||
|
begin
|
|||
|
Boundary.Init(InitX, InitY, InitWidth, SetTrap);
|
|||
|
end;
|
|||
|
|
|||
|
function LeftBound.Collide(var B : Ball) : Boolean;
|
|||
|
begin
|
|||
|
Collide := False;
|
|||
|
if (B.NextX <= X) and (B.NextY >= Y) and (B.NextY <= Y + Width) then
|
|||
|
begin
|
|||
|
B.ReverseX;
|
|||
|
Collide := True;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
constructor UpperBound.Init(InitX, InitY, InitWidth : Integer;
|
|||
|
SetTrap : Boolean);
|
|||
|
begin
|
|||
|
Boundary.Init(InitX, InitY, InitWidth, SetTrap);
|
|||
|
end;
|
|||
|
|
|||
|
function UpperBound.Collide(var B : Ball) : Boolean;
|
|||
|
begin
|
|||
|
Collide := False;
|
|||
|
if (B.NextY <= Y) and (B.NextX >= X) and (B.NextX <= X + Width) then
|
|||
|
begin
|
|||
|
B.ReverseY;
|
|||
|
Collide := True;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
constructor RightBound.Init(InitX, InitY, InitWidth : Integer;
|
|||
|
SetTrap : Boolean);
|
|||
|
begin
|
|||
|
Boundary.Init(InitX, InitY, InitWidth, SetTrap);
|
|||
|
end;
|
|||
|
|
|||
|
function RightBound.Collide(var B : Ball) : Boolean;
|
|||
|
begin
|
|||
|
Collide := False;
|
|||
|
if (B.NextX >= X) and (B.NextY >= Y) and (B.NextY <= Y + Width) then
|
|||
|
begin
|
|||
|
B.ReverseX;
|
|||
|
Collide := True;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
constructor LowerBound.Init(InitX, InitY, InitWidth : Integer;
|
|||
|
SetTrap : Boolean);
|
|||
|
begin
|
|||
|
Boundary.Init(InitX, InitY, InitWidth, SetTrap);
|
|||
|
end;
|
|||
|
|
|||
|
function LowerBound.Collide(var B : Ball) : Boolean;
|
|||
|
begin
|
|||
|
Collide := False;
|
|||
|
if (B.NextY >= Y) and (B.NextX >= X) and (B.NextX <= X + Width) then
|
|||
|
begin
|
|||
|
B.ReverseY;
|
|||
|
Collide := True;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
end.
|
|||
|
|