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