HP Prime for All
English
Русский
Name | Pong |
Description | The famous PONG game, now available on the Prime. Features: 3 modes (1 Player, 2 Players, and demo), 3 difficulty levels (beginner, intermediate, expert), and black and white graphics for the real retro feeling. |
Author | Jurgen Keller |
Source code formatted by website engine
BEGIN
// B&W for the real retro feeling
Color0 := RGB(0, 0, 0);
Color1 := RGB(255, 255, 255);
// Outer court
Fx1 := 4;
Fy1 := 4;
Fx2 := 320-Fx1;
Fy2 := 240-Fy1;
// center position
Mx := (Fx1+Fx2) / 2;
My := (Fy1+Fy2) / 2;
// control keys
Kup := {32, 35}; // '7' and '/'
Kdn := {47, 50}; // '0' and '+'
// score bitmap size and scaling
Scx := 7;
Scy := 5;
Scf := 4;
// difficulty settings
// (ball speed factor, paddle length,
// auto paddle speed)
Difficulty := {
{2.2, 46, 1.4},
{2.8, 36, 2.1},
{3.4, 28, 2.8}
};
// others
Win := 10;
END;
//-------------------------------------
// Clear the screen.
Cls()
BEGIN
RECT_P(Color0);
END;
//-------------------------------------
// Wait until user presses a key from
// a list. Returns index of the key.
//
ReadKey(Keys)
BEGIN
LOCAL I, J, K;
I := -1;
WHILE I < 0 DO
K := GETKEY();
FOR J FROM 1 TO SIZE(Keys) DO
IF K == Keys[J] THEN
I := J;
END;
END;
END;
RETURN I;
END;
//-------------------------------------
// Draw a multi-line text on G.
// Parameters:
// Txt - a list of strings;
// use an empty string to
// start new paragraph
// X, Y - top left position
// Height - line height in pixels
// Font - font size
//
DrawText(G, Txt, X, Y, Height, Font)
BEGIN
LOCAL I, V := Y;
FOR I FROM 1 TO SIZE(Txt) DO
IF SIZE(Txt[I]) > 0 THEN
TEXTOUT_P(Txt[I], G, X, V, Font, Color1, 320, Color0);
V := V+Height;
ELSE // new paragraph
V := V+Height/2;
END;
END;
END;
//-------------------------------------
// Draw the PONG logo on G.
//
DrawLogo(G)
BEGIN
// PONG bitmap
DIMGROB_P(G6, 20, 5,
{#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF0000:64h,
#7FFF7FFF00007FFF:64h, #7FFF00007FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #7FFF000000007FFF:64h,
#7FFF0000:64h, #7FFF00007FFF:64h,
#7FFF00007FFF0000:64h, #0:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF0000:64h,
#7FFF00007FFF:64h, #7FFF00007FFF0000:64h,
#7FFF7FFF0000:64h, #7FFF:64h,
#7FFF0000:64h, #7FFF00007FFF:64h,
#7FFF00007FFF0000:64h, #7FFF00000000:64h,
#7FFF:64h, #7FFF7FFF7FFF0000:64h,
#7FFF00007FFF:64h, #7FFF00007FFF0000:64h,
#7FFF7FFF7FFF:64h});
BLIT_P(G, 60, 30, 260, 80, G6, 0, 0, 20, 5);
END;
//-------------------------------------
// Draw the court on G.
//
DrawCourt(G, Mode)
BEGIN
LOCAL X, Y, S;
// top and bottom border
RECT_P(G, Fx1, Fy1, Fx2-1, Iy1-1, Color1);
RECT_P(G, Fx1, Iy2, Fx2-1, Fy2-1, Color1);
// dotted line in the middle
S := Iy1-Fy1;
X := (Fx1+Fx2+1-W) / 2;
FOR Y FROM Iy1+1 TO Iy2 STEP 2*S DO
RECT_P(G, X, Y, X+S-1, Y+S-1, Color1);
END;
IF Mode == 3 THEN
TEXTOUT_P("Press Esc to quit demo mode", G, 68, 210, 3, Color1, 320, Color0);
END;
END;
//-------------------------------------
// Draw current score on G.
//
DrawScore(G)
BEGIN
LOCAL W := Scx*Scf;
LOCAL H := Scy*Scf;
LOCAL Y1, Y2;
Y1 := Iy1+8;
Y2 := Score[1]*H;
BLIT_P(G, 118, Y1, G3, 0, Y2, W, Y2+H);
Y2 := Score[2]*H;
BLIT_P(G, 174, Y1, G3, W, Y2, 2*W, Y2+H);
END;
//-------------------------------------
// Prepare graphical objects for fast
// drawing.
// Usage of graphics objects:
// G1 - used to assemble a frame
// G2 - court
// G3 - score bitmap
// G4 - paddle
// G5 - ball
//
MakeGrobs()
BEGIN
LOCAL Scw, Sch;
DIMGROB_P(G1, 320, 240, Color0);
// Court
DIMGROB_P(G2, 320, 240, Color0);
DrawCourt(G2, Mode);
// Paddle
DIMGROB_P(G4, Psx, Psy, Color1);
// Ball
DIMGROB_P(G5, Bsx, Bsy, Color1);
// Score Bitmap
DIMGROB_P(G6, 14, 55,
{#7FFF000000000000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #0:64h,
#7FFF0000:64h, #7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF7FFF00000000:64h, #7FFF00000000:64h,
#0:64h, #7FFF0000:64h,
#7FFF7FFF:64h, #7FFF:64h,
#7FFF000000000000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #0:64h,
#7FFF7FFF00000000:64h, #7FFF7FFF00000000:64h,
#0:64h, #0:64h,
#7FFF0000:64h, #7FFF0000:64h,
#0:64h, #7FFF000000000000:64h,
#7FFF000000000000:64h, #0:64h,
#0:64h, #7FFF0000:64h,
#7FFF0000:64h, #0:64h,
#7FFF000000000000:64h, #7FFF000000000000:64h,
#0:64h, #7FFF000000000000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF:64h,
#0:64h, #0:64h,
#7FFF:64h, #7FFF:64h,
#7FFF000000000000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #0:64h,
#7FFF0000:64h, #7FFF0000:64h,
#0:64h, #7FFF000000000000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF:64h,
#0:64h, #7FFF7FFF7FFF0000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF:64h,
#0:64h, #7FFF00000000:64h,
#7FFF00000000:64h, #0:64h,
#7FFF7FFF00000000:64h, #7FFF7FFF00007FFF:64h,
#7FFF:64h, #0:64h,
#7FFF00000000:64h, #7FFF00000000:64h,
#0:64h, #7FFF7FFF7FFF0000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF:64h,
#7FFF000000000000:64h, #7FFF7FFF00000000:64h,
#7FFF00000000:64h, #0:64h,
#7FFF0000:64h, #7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF:64h,
#0:64h, #0:64h,
#7FFF:64h, #7FFF:64h,
#0:64h, #7FFF00000000:64h,
#7FFF00000000:64h, #0:64h,
#7FFF7FFF7FFF0000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF000000000000:64h, #0:64h,
#0:64h, #7FFF7FFF7FFF0000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF:64h,
#0:64h, #7FFF00000000:64h,
#7FFF00000000:64h, #0:64h,
#7FFF7FFF7FFF0000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF:64h,
#0:64h, #7FFF0000:64h,
#7FFF0000:64h, #0:64h,
#7FFF000000000000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #0:64h,
#7FFF0000:64h, #7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF:64h,
#0:64h, #7FFF7FFF7FFF0000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF:64h,
#0:64h, #7FFF00000000:64h,
#7FFF00000000:64h, #0:64h,
#0:64h, #7FFF:64h,
#7FFF:64h, #0:64h,
#7FFF00000000:64h, #7FFF00000000:64h,
#0:64h, #0:64h,
#7FFF:64h, #7FFF:64h,
#7FFF000000000000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #0:64h,
#7FFF0000:64h, #7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF7FFF:64h,
#0:64h, #7FFF0000:64h,
#7FFF7FFF:64h, #7FFF:64h,
#7FFF000000000000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF7FFF7FFF:64h, #0:64h,
#7FFF7FFF7FFF0000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF:64h, #7FFF000000000000:64h,
#7FFF7FFF00000000:64h, #7FFF00000000:64h,
#0:64h, #7FFF7FFF7FFF0000:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF:64h,
#0:64h, #7FFF00000000:64h,
#7FFF00000000:64h, #0:64h,
#7FFF7FFF7FFF0000:64h, #7FFF7FFF7FFF7FFF:64h,
#7FFF:64h, #7FFF00007FFF7FFF:64h,
#7FFF7FFF7FFF7FFF:64h, #7FFF7FFF00007FFF:64h,
#7FFF00007FFF7FFF:64h, #7FFF0000:64h,
#7FFF00007FFF:64h, #7FFF000000007FFF:64h,
#7FFF00007FFF0000:64h, #7FFF00000000:64h,
#7FFF00007FFF:64h, #7FFF00007FFF0000:64h,
#7FFF0000:64h, #7FFF00007FFF:64h,
#7FFF000000007FFF:64h, #7FFF00007FFF0000:64h,
#7FFF7FFF7FFF:64h, #7FFF7FFF00007FFF:64h,
#7FFF7FFF:64h});
Scw := 2*Scx; // 2 digits per score
Sch := 11*Scy; // 11 scores (0..10)
DIMGROB_P(G3, Scw*Scf, Sch*Scf, Color0);
BLIT_P(G3, 0, 0, Scw*Scf, Sch*Scf, G6, 0, 0, Scw, Sch);
END;
//-------------------------------------
// Handle collision of ball with top
// and bottom court boundaries.
//
HandleWallCollision()
BEGIN
IF Bpy < BpyMin THEN
Bpy := 2*BpyMin-Bpy;
Bmy := -Bmy;
ELSE
IF Bpy > BpyMax THEN
Bpy := 2*BpyMax-Bpy;
Bmy := -Bmy;
END;
END;
END;
//-------------------------------------
// Handle collision of ball with one
// of the paddles.
// The new Y component of the ball's
// motion vector depends on where the
// ball hit the paddle. The hit point
// is in the range:
// [ - (Psy+Bsy) / 2, +(Psy+Bsy) / 2]
// That value is scaled such that the
// angle is not more than about 50
// degrees (TAN(angle)~ = 1.2).
//
HandlePaddleCollision()
BEGIN
LOCAL F := -1.2*Difficulty[Level, 1];
IF Bpx < BpxMin THEN
Out := 1;
IF Bpy > Ppy[1]-Bsy THEN
IF Bpy < Ppy[1]+Psy THEN
Bpx := 2*BpxMin-Bpx;
Bmx := -Bmx;
Bmy := Ppy[1]-Bpy + (Psy-Bsy) / 2;
Bmy := F*Bmy / ((Psy+Bsy) / 2);
Out := 0;
END;
END;
ELSE
IF Bpx > BpxMax THEN
Out := 2;
IF Bpy > Ppy[2]-Bsy THEN
IF Bpy < Ppy[2]+Psy THEN
Bpx := 2*BpxMax-Bpx;
Bmx := -Bmx;
Bmy := Ppy[2]-Bpy + (Psy-Bsy) / 2;
Bmy := F*Bmy / ((Psy+Bsy) / 2);
Out := 0;
END;
END;
END;
END;
END;
//-------------------------------------
// Move the ball. Handle collisions
// with court and paddles.
//
MoveBall()
BEGIN
Bpx := Bpx+Bmx;
Bpy := Bpy+Bmy;
HandleWallCollision();
IF NOT Out THEN
HandlePaddleCollision();
END;
END;
//-------------------------------------
// Let the computer move a paddle.
// Strategy if ball leaves:
// - > choose a random point around
// the middle and move towards
// that point
// Strategy if ball approaches:
// - > choose a random hit point on
// the paddle, then align that
// point to the ball
//
MoveAutoPaddle(Nr)
BEGIN
IF (Nr == 1 AND Bmx < 0) OR (Nr == 2 AND Bmx > 0) THEN
IF HitPt[Nr] < 0 THEN
// hit point might be outside the paddle
// so that computer sometimes misses
HitPt[Nr] := RANDOM(Psy+4) - 2;
END;
IF Bpy < Ppy[Nr]+HitPt[Nr]-2 THEN
Ppy[Nr] := MAX(Ppy[Nr]-Pmy[Nr], PpyMin);
ELSE
IF Bpy > Ppy[Nr]+HitPt[Nr]+2 THEN
Ppy[Nr] := MIN(Ppy[Nr]+Pmy[Nr], PpyMax);
END;
END;
ELSE
IF HitPt[Nr] >= 0 THEN
HitPt[Nr] := - (My-Psy/2+RANDOM(80) - 40);
END;
IF Ppy[Nr] < -HitPt[Nr]-2 THEN
Ppy[Nr] := Ppy[Nr]+Pmy[Nr]/2;
ELSE
IF Ppy[Nr] > -HitPt[Nr]+2 THEN
Ppy[Nr] := Ppy[Nr]-Pmy[Nr]/2;
END;
END;
END;
END;
//-------------------------------------
// Move a user controlled paddle.
// A short acceleration phase allows
// for positioning the paddle more
// precisely.
//
MoveUserPaddle(Nr)
BEGIN
LOCAL PmyMax := 1.5*Difficulty[Level, 3];
IF ISKEYDOWN(Kup[Nr]) THEN
Ppy[Nr] := MAX(Ppy[Nr]-Pmy[Nr], PpyMin);
Pmy[Nr] := MIN(1.1*Pmy[Nr], PmyMax);
ELSE
IF ISKEYDOWN(Kdn[Nr]) THEN
Ppy[Nr] := MIN(Ppy[Nr]+Pmy[Nr], PpyMax);
Pmy[Nr] := MIN(1.1*Pmy[Nr], PmyMax);
ELSE
Pmy[Nr] := PmyMax/4;
END;
END;
END;
//-------------------------------------
// Move a paddle controlled by user or
// computer.
//
MovePaddle(Nr)
BEGIN
IF (Mode == 1 AND Nr == 1) OR (Mode == 3) THEN
MoveAutoPaddle(Nr);
ELSE
MoveUserPaddle(Nr);
END;
END;
//-------------------------------------
// Draw the current state of the game.
//
UpdateScreen()
BEGIN
// court
BLIT_P(G1, G2);
// score
DrawScore(G1);
// paddles
BLIT_P(G1, Ppx[1], Ppy[1], G4, 0, 0, Psx, Psy);
BLIT_P(G1, Ppx[2], Ppy[2], G4, 0, 0, Psx, Psy);
// ball
BLIT_P(G1, Bpx, Bpy, G5, 0, 0, Bsx, Bsy);
// now display the frame
BLIT_P(G0, G1);
// for debugging
// REPEAT UNTIL ISKEYDOWN(30);
END;
//-------------------------------------
// Update score after ball went out
// of the court. Player scores if
// opponent missed the ball.
//
UpdateScore()
BEGIN
IF Mode > 0 THEN
Server := 3-Out;
Score[Server] := Score[Server]+1;
END;
END;
//-------------------------------------
// Position the ball in the middle and
// choose a random ball motion towards
// the opponent.
//
PrepareService()
BEGIN
Bpx := (2*Mx+2-Bsx) / 2;
Bpy := (2*My-Bsy) / 2;
// not more than ~20 degrees
Bmx := Difficulty[Level, 1];
Bmy := Bmx*RANDINT(20) / 50;
IF RANDINT THEN
Bmy := -Bmy;
END;
IF Server == 2 THEN
Bmx := -Bmx;
END;
Out := 0;
END;
//-------------------------------------
// Center paddles and prepare service.
//
PrepareRally()
BEGIN
Ppy[1] := My-Psy/2;
Ppy[2] := Ppy[1];
PrepareService();
END;
//-------------------------------------
// Rally ends if
// - ball is outside the court
// - or if user quits demo mode
//
IsRallyEnd()
BEGIN
IF Mode == 3 AND ISKEYDOWN(4) THEN
// user requested to leave demo mode
Mode := 0;
END;
RETURN NOT Mode OR (Out AND (Bpx < -Bsx OR Bpx > 320));
END;
//-------------------------------------
// Play a single rally.
//
PlayRally()
BEGIN
PrepareRally();
UpdateScreen();
IF Mode <> 3 THEN
// allow players to prepare
WAIT(1.5);
END;
REPEAT
MoveBall();
MovePaddle(1);
MovePaddle(2);
UpdateScreen();
UNTIL IsRallyEnd();
UpdateScore();
UpdateScreen();
END;
//-------------------------------------
// Prepare a match. Initialize all the
// game variables. Some of them depend
// on the difficulty level, e.g., the
// size of the paddles.
//
PrepareMatch()
BEGIN
// ball size
Bsx := 8;
Bsy := 8;
// paddle size
Psx := 8;
Psy := Difficulty[Level, 2];
// inner court
Ix1 := Fx1+Psx+4;
Ix2 := Fx2-Psx-4;
Iy1 := Fy1+2;
Iy2 := Fy2-2;
// paddle positions
Ppx := {Ix1-Psx, Ix2};
Ppy := {0, 0};
// paddle speed
Pmy := {Difficulty[Level, 3], Difficulty[Level, 3]};
// min/max ball position
BpxMin := Ppx[1]+Psx;
BpxMax := Ppx[2]-Bsx;
BpyMin := Iy1;
BpyMax := Iy2-Bsy;
// min/max paddle position
PpyMin := Iy1+1;
PpyMax := Iy2-Psy-1;
// server
IF Mode == 1 THEN
Server := 2;
ELSE
Server := 1;
END;
// reset score
Score := {0, 0};
// Prepare bitmaps for drawing
MakeGrobs();
// misc stuff
HitPt := {-1, -1};
END;
//-------------------------------------
// Match ends if
// - one of the players reached 10 pts
// - or if user quits demo mode
//
IsMatchEnd()
BEGIN
RETURN NOT Mode OR (Score[1] == Win OR Score[2] == Win);
END;
//-------------------------------------
// Presentation ceremony.
//
ShowWinner()
BEGIN
LOCAL X := 174;
IF Score[1] == Win THEN
X := 64;
END;
TEXTOUT_P("WINNER", G0, X, 40, 7, Color1);
END;
//-------------------------------------
// Return
// - true if user wants to play
// another game
// - false if user wants to quit
//
PlayAgain()
BEGIN
LOCAL Again := 0;
CASE
IF Mode == 1 OR Mode == 2 THEN
TEXTOUT_P("Press Enter to play again or Esc to quit", G0, 38, 200, 3, Color1, 320, Color0);
IF ReadKey({4, 30}) == 2 THEN
Again := 1;
END;
END;
IF Mode == 3 THEN
// still in demo mode
Again := WAIT(1);
END;
END;
RETURN Again;
END;
//-------------------------------------
// Play a match (a series of rallys)
// and show the winner at the end.
//
PlayMatch()
BEGIN
REPEAT
PrepareMatch();
REPEAT
PlayRally();
UNTIL IsMatchEnd();
ShowWinner();
UNTIL NOT PlayAgain();
END;
//-------------------------------------
// Display help on how to play PONG.
//
ShowInstructions()
BEGIN
LOCAL Cap := {
{"ONE PLAYER INSTRUCTIONS"},
{"TWO PLAYER INSTRUCTIONS"}};
LOCAL Txt := {
{"Control the paddle on the right hand side",
"of the screen using your keyboard.",
"Use '/' to move the paddle up and '+' to",
"move the paddle down.",
"",
"Points are scored when your opponent",
"misses the ball. First player to reach 10",
"points wins the game.",
"",
"Press Enter to continue"},
{"Player 1 controls the left hand paddle",
"using the '7' (up) and '0' (down) keys.",
"Player 2 controls the right hand paddle",
"using the '/' (up) and '+' (down) keys.",
"",
"Points are scored when a player misses",
"the ball. First player to reach 10 points",
"wins the game.",
"",
"Press Enter to continue"}};
IF Mode == 1 OR Mode == 2 THEN
Cls();
DrawText(G0, Cap[Mode], 24, 24, 18, 4);
DrawText(G0, Txt[Mode], 24, 55, 18, 3);
ReadKey({30});
END;
END;
//-------------------------------------
// Let the user choose the difficulty
// level (beginner, intermediate, or
// expert).
//
ChooseLevel()
BEGIN
LOCAL Txt := {
"[1] Beginner",
"[2] Intermediate",
"[3] Expert",
"Esc to quit"};
IF Mode == 3 THEN
// choose 'beginner' for demo
Level := 1;
ELSE
Cls();
DrawLogo(G0);
DrawText(G0, Txt, 100, 110, 25, 5);
Level := ReadKey({4, 42, 43, 44}) - 1;
END;
END;
//-------------------------------------
// Let the user choose the play mode
// (1 Player, 2 Players, Demo).
//
ChooseMode()
BEGIN
LOCAL Txt := {
"[1] 1 Player",
"[2] 2 Player",
"[3] Demo Mode",
"Esc to quit"};
Cls();
DrawLogo(G0);
DrawText(G0, Txt, 100, 110, 25, 5);
Mode := ReadKey({4, 42, 43, 44}) - 1;
END;
//-------------------------------------
// The main function.
//
EXPORT PONG()
BEGIN
Initialize();
WHILE ChooseMode() > 0 DO
IF ChooseLevel() > 0 THEN
ShowInstructions();
PlayMatch();
END;
END;
END;