302 lines
6.5 KiB
Plaintext
302 lines
6.5 KiB
Plaintext
|
|
|||
|
{ Turbo Breakout }
|
|||
|
{ Copyright (c) 1989 by Borland International, Inc. }
|
|||
|
|
|||
|
program Breakout;
|
|||
|
{ Turbo Pascal 5.5 object-oriented example.
|
|||
|
|
|||
|
This is a version of the classic arcade game, Breakout.
|
|||
|
|
|||
|
SCREEN.PAS
|
|||
|
COUNT.PAS
|
|||
|
BRICKS.PAS
|
|||
|
BOUNDS.PAS
|
|||
|
WALLS.PAS
|
|||
|
BREAKOUT.PAS
|
|||
|
|
|||
|
To build an executable file, compile from the command line with:
|
|||
|
|
|||
|
tpc /b breakout
|
|||
|
|
|||
|
or load BREAKOUT.PAS into the integrated development
|
|||
|
environment and press F9.
|
|||
|
|
|||
|
When testing the program, you may want to force the paddle to
|
|||
|
follow the ball, so you'll never miss. The program contains
|
|||
|
conditional compilation directives to produce this version, and
|
|||
|
you can build it from the command line with:
|
|||
|
|
|||
|
tpc /DTest breakout
|
|||
|
|
|||
|
or load BREAKOUT.PAS into the integrated development
|
|||
|
environment, select Alt-O/C/C, type 'Test' (without the quotes,
|
|||
|
of course) followed by the Enter key, then select Alt-C/B to
|
|||
|
rebuild the executable file.
|
|||
|
}
|
|||
|
|
|||
|
uses Screen, Count, Bricks, Bounds, Walls, Crt, Dos;
|
|||
|
|
|||
|
var
|
|||
|
ss : SaveScreen;
|
|||
|
w : Wall;
|
|||
|
b : Ball;
|
|||
|
p : Paddle;
|
|||
|
Speed : LimitCounter;
|
|||
|
Left : LeftBound;
|
|||
|
Top : UpperBound;
|
|||
|
Right : RightBound;
|
|||
|
Bottom : LowerBound;
|
|||
|
Obstacles : ObstacleList;
|
|||
|
PaddleMsg,
|
|||
|
SpeedMsg,
|
|||
|
StartMsg,
|
|||
|
QuitMsg,
|
|||
|
PauseMsg1,
|
|||
|
PauseMsg2,
|
|||
|
TypeMsg : TextString;
|
|||
|
Score : Counter;
|
|||
|
Highest : Counter;
|
|||
|
Balls : DownCounter;
|
|||
|
X : Integer;
|
|||
|
Finished : Boolean;
|
|||
|
FirstGame : Boolean;
|
|||
|
TypeInc,
|
|||
|
ch : Char;
|
|||
|
|
|||
|
procedure Startup;
|
|||
|
begin
|
|||
|
{ First set up the screen and the cursor }
|
|||
|
ss.Init;
|
|||
|
TextBackground(BLACK);
|
|||
|
ClrScr;
|
|||
|
|
|||
|
{ Create the boundaries of the playfield }
|
|||
|
Left.Init(0, 0, 27, False);
|
|||
|
Top.Init(0, 0, 82, False);
|
|||
|
Right.Init(81, 0, 27, False);
|
|||
|
Bottom.Init(0, 24, 82, True);
|
|||
|
|
|||
|
{ Initialize the score displays }
|
|||
|
Score.Init(0, 65, 24, 'Score', 15);
|
|||
|
Score.Show;
|
|||
|
Highest.Init(0, 60, 25, 'High Score', 14);
|
|||
|
Highest.Show;
|
|||
|
|
|||
|
{ Set up the various menu messages }
|
|||
|
PauseMsg1.Init(31, 18, 'Paused. Press any', 15);
|
|||
|
PauseMsg2.Init(31, 19, ' key to continue.', 15);
|
|||
|
SpeedMsg.Init(5, 23, #24 + #25 + ' to change speed', 14);
|
|||
|
StartMsg.Init(5, 24, #17 + #196 + #217 + ' to begin game', 14);
|
|||
|
PaddleMsg.Init(5, 24, #27 + #26 + ' to move paddle', 14);
|
|||
|
QuitMsg.Init(5, 25, 'ESC to quit', 14);
|
|||
|
QuitMsg.Show;
|
|||
|
|
|||
|
{ Set up the information messages }
|
|||
|
Balls.Init(5, 40, 24, -1, 'Balls', 15);
|
|||
|
Balls.Show;
|
|||
|
Speed.Init(1, 40, 25, 1, 10, 'Speed', 14);
|
|||
|
Speed.Show;
|
|||
|
|
|||
|
{ Build the wall }
|
|||
|
w.Init(1, 1, 16, 10);
|
|||
|
w.Show;
|
|||
|
|
|||
|
{ Need to initialize these, even though we're going to move them later }
|
|||
|
b.Init(10, 22, 1, -1, YELLOW);
|
|||
|
p.Init(8, 23, WHITE);
|
|||
|
|
|||
|
{ Put the various obstacles into a list. We don't really need
|
|||
|
to do this, but it makes changing things around much easier }
|
|||
|
Obstacles.Init;
|
|||
|
Obstacles.Append(@p);
|
|||
|
Obstacles.Append(@w);
|
|||
|
Obstacles.Append(@Left);
|
|||
|
Obstacles.Append(@Top);
|
|||
|
Obstacles.Append(@Right);
|
|||
|
Obstacles.Append(@Bottom);
|
|||
|
|
|||
|
TypeMsg.Init(22, 12, 'Increase typematic rate? (y/n) ', WHITE);
|
|||
|
TypeMsg.Show;
|
|||
|
repeat
|
|||
|
TypeInc := UpCase(ReadKey);
|
|||
|
until (TypeInc = 'Y') or (TypeInc = 'N');
|
|||
|
TypeMsg.Hide;
|
|||
|
|
|||
|
if TypeInc = 'Y' then
|
|||
|
ss.Speedup;
|
|||
|
|
|||
|
ss.SetCursor($2000);
|
|||
|
Randomize;
|
|||
|
FirstGame := True;
|
|||
|
end;
|
|||
|
|
|||
|
procedure NewGame;
|
|||
|
begin
|
|||
|
Balls.Reset;
|
|||
|
Score.Reset;
|
|||
|
if not FirstGame then
|
|||
|
w.Reset;
|
|||
|
X := Random(78) + 3;
|
|||
|
b.MoveTo(X, 22);
|
|||
|
p.MoveTo(X-2, 23);
|
|||
|
b.Show;
|
|||
|
p.Show;
|
|||
|
Balls.Decrement;
|
|||
|
FirstGame := False;
|
|||
|
end;
|
|||
|
|
|||
|
{ This procedure handles keystrokes between games.
|
|||
|
It returns False if the user presses ESC, otherwise it returns True. }
|
|||
|
function MainMenu : Boolean;
|
|||
|
var
|
|||
|
Done : Boolean;
|
|||
|
begin
|
|||
|
MainMenu := True;
|
|||
|
Done := False;
|
|||
|
SpeedMsg.Show;
|
|||
|
StartMsg.Show;
|
|||
|
while not Done do
|
|||
|
begin
|
|||
|
ch := ReadKey;
|
|||
|
case ch of
|
|||
|
Chr(27) :
|
|||
|
begin
|
|||
|
MainMenu := False;
|
|||
|
Done := True;
|
|||
|
end;
|
|||
|
#13 : Done := True;
|
|||
|
#0 :
|
|||
|
begin
|
|||
|
ch := ReadKey;
|
|||
|
if Ord(ch) = 72 then
|
|||
|
Speed.Increment
|
|||
|
else if Ord(ch) = 80 then
|
|||
|
Speed.Decrement;
|
|||
|
end;
|
|||
|
end;
|
|||
|
end;
|
|||
|
SpeedMsg.Hide;
|
|||
|
StartMsg.Hide;
|
|||
|
end;
|
|||
|
|
|||
|
{ This procedure handles keystrokes while the game is in progress }
|
|||
|
procedure ProcessKeyStroke;
|
|||
|
|
|||
|
{ Pause the game }
|
|||
|
procedure Pause;
|
|||
|
begin
|
|||
|
PauseMsg1.Show;
|
|||
|
PauseMsg2.Show;
|
|||
|
ch := ReadKey;
|
|||
|
if KeyPressed then
|
|||
|
ch := ReadKey; { Swallow extended keystrokes }
|
|||
|
PauseMsg1.Hide;
|
|||
|
PauseMsg2.Hide;
|
|||
|
b.Show;
|
|||
|
end;
|
|||
|
|
|||
|
begin
|
|||
|
ch := ReadKey;
|
|||
|
case ch of
|
|||
|
Chr(27) : Finished := True;
|
|||
|
Chr(0) :
|
|||
|
begin
|
|||
|
ch := ReadKey;
|
|||
|
{$IFNDEF Test}
|
|||
|
case Ord(ch) of
|
|||
|
75: p.MoveTo(p.GetX - 1, p.GetY); { Left Arrow }
|
|||
|
77: p.MoveTo(p.GetX + 1, p.GetY); { Right Arrow }
|
|||
|
else
|
|||
|
Pause;
|
|||
|
end;
|
|||
|
{$ELSE}
|
|||
|
Pause;
|
|||
|
{$ENDIF}
|
|||
|
end
|
|||
|
else
|
|||
|
Pause;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
{ This procedure checks for collisions with any of the obstacles
|
|||
|
and updates the screen accordingly. }
|
|||
|
procedure Update;
|
|||
|
var
|
|||
|
Offset : Integer;
|
|||
|
begin
|
|||
|
if Obstacles.CheckCollisions(b, Score) then
|
|||
|
begin
|
|||
|
b.MoveY;
|
|||
|
p.MoveTo(b.GetX - 2, p.GetY);
|
|||
|
sound(150);
|
|||
|
Delay(300);
|
|||
|
nosound;
|
|||
|
Balls.Decrement;
|
|||
|
while KeyPressed do
|
|||
|
ch := ReadKey;
|
|||
|
end;
|
|||
|
|
|||
|
b.MoveX;
|
|||
|
b.MoveY;
|
|||
|
|
|||
|
{$IFDEF Test}
|
|||
|
p.MoveTo(b.NextX -2, p.GetY);
|
|||
|
{$ENDIF}
|
|||
|
end;
|
|||
|
|
|||
|
{ This procedure cleans up when we're exiting from the program }
|
|||
|
procedure ShutDown;
|
|||
|
begin
|
|||
|
b.Hide;
|
|||
|
Obstacles.Hide;
|
|||
|
Balls.Hide;
|
|||
|
Score.Hide;
|
|||
|
|
|||
|
Obstacles.Done;
|
|||
|
|
|||
|
ss.Restore;
|
|||
|
if TypeInc = 'Y' then
|
|||
|
ss.Slowdown;
|
|||
|
ClrScr;
|
|||
|
end;
|
|||
|
|
|||
|
{ This procedure plays a game. The main loop allows up to ten keystrokes,
|
|||
|
then moves the ball and checks for collisions }
|
|||
|
procedure Play;
|
|||
|
var
|
|||
|
KeyLoops : Integer;
|
|||
|
begin
|
|||
|
NewGame;
|
|||
|
{$IFNDEF Test}
|
|||
|
PaddleMsg.Show;
|
|||
|
{$ENDIF}
|
|||
|
Finished := False;
|
|||
|
KeyLoops := 0;
|
|||
|
repeat
|
|||
|
if KeyPressed then
|
|||
|
ProcessKeyStroke;
|
|||
|
Inc(KeyLoops);
|
|||
|
if (KeyLoops = 10) and not Finished then
|
|||
|
begin
|
|||
|
KeyLoops := 0;
|
|||
|
UpDate;
|
|||
|
end;
|
|||
|
Delay(12 - Speed.GetValue);
|
|||
|
until Finished or Balls.Last;
|
|||
|
PaddleMsg.Hide;
|
|||
|
end;
|
|||
|
|
|||
|
begin
|
|||
|
Startup;
|
|||
|
while MainMenu do
|
|||
|
begin
|
|||
|
Play;
|
|||
|
Balls.Reset;
|
|||
|
b.Hide;
|
|||
|
p.Hide;
|
|||
|
if Score.GetValue > Highest.GetValue then
|
|||
|
Highest.SetValue(Score.GetValue);
|
|||
|
end;
|
|||
|
ShutDown;
|
|||
|
end.
|
|||
|
|