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.
|
||
|