'======================================== '| PONGadelic! A shameless Pong clone | '| @ J. Lewis, December 2018 | '| Rev 0.1 | '======================================== ' Include useful predefined constants INCLUDE "constants.bas" ' Title screen code. INCLUDE "PONGadelic_title.bas" Main: 'When the user presses any key on the title screen, Main: is called. CLS 'Set background color stack to Black with White borders MODE SCREEN_COLOR_STACK, STACK_BLACK, STACK_CYAN, STACK_BROWN, STACK_YELLOW 'I have no idea how to use anything after STACK_BLACK BORDER BORDER_BLACK WAIT 'Define some variables DIM wallColor, fieldColor, leftPaddleColor, rightPaddleColor, paddleSpeed, ballColor, ballSpeed, ballBoost, ballX, ballY, scoreLeft, scoreRight, scoredAgainst SIGNED ballDirX, ballDirY 'signed varaibles for ball directions scoredAgainst = 1 'flag to hold which player just got scored against. 1=P1=LEFT, 2=P2=RIGHT scoreLeft = 0 'score of the left player scoreRight = 0 'score of the right player paddleSpeed = 2 'movement speed of the paddles ballSpeed = 2 'movement speed of the ball ballBoost = 0 'varable to hold movement "boost" for the ball, so when it's moving in X-only, we can add some speed, so the distance/time is the same... ballDirX = 0 'ball's X-direction. -1 = LEFT, 0 = NONE, 1 = RIGHT ballDirY = 0 'ball's Y-direction. -1 = UP, 0 = NONE, 1 DOWN DEFINE DEF00,5,sprites 'load 5 sprites into GRAM. 'SPR00 = Paddle upper 'SPR01 = Paddle middle (use ZOOMY2 unless you wany tiny broken paddles) 'SPR02 = Paddle lower 'SPR03 = Ball (frame 1; default frame) 'SPR04 = Ball (frame 2; alternate frame) WAIT 'do we always have to WAIT after GRAM loading? leftPaddleX = 10 'starting X-coord for left paddle leftPaddleY = 50 'starting Y-coord for left paddle rightPaddleX = 157 'starting X-coord for right paddle rightPaddleY = 50 'starting Y-coord for right paddle nextPoint:'We will jump to here whenever a point is scored, to reset the board etc.-------------------------------------------------------------------------------- 'This part is just getting colors for the Ball, Walls, and Paddles (2). 'The rules are that none of the colors can be the same and none of the colors can be black (since the background is black). getBallColor: ballColor = (RAND(32) AND SPR_WHITE) IF (ballColor = SPR_BLACK) THEN GOTO getBallColor WAIT getWallColor: wallColor = (RAND(32) AND CS_WHITE) IF (wallColor = CS_BLACK OR wallColor = ballColor) THEN GOTO getWallColor WAIT getLeftPaddleColor: leftPaddleColor = (RAND(32) AND CS_WHITE) leftPaddleSpriteColor = (RAND(32) AND SPR_WHITE) IF (leftPaddleColor = CS_BLACK OR leftPaddleColor = ballColor OR leftPaddleColor = wallColor) THEN GOTO getLeftPaddleColor WAIT getRightPaddleColor: rightPaddleColor = (RAND(32) AND CS_WHITE) rightPaddleSpriteColor = (RAND(32) AND SPR_WHITE) IF (rightPaddleColor = CS_BLACK OR rightPaddleColor = ballColor OR rightPaddleColor = wallColor OR rightPaddleColor = leftPaddleColor) THEN GOTO getRightPaddleColor 'center the ball in the field ballX = 83 ballY = 55 'center the paddles leftPaddleY = 50 rightPaddleY = 50 'print wall backgrounds, leaving one card(?) above to leave room for score and messages FOR I=20 TO 39 PRINT AT I COLOR wallColor,"\200" PRINT AT I+200 COLOR wallColor,"\203" NEXT I PRINT AT (SCREENPOS(4, 0)) COLOR wallColor,"\190" 'left score devider PRINT AT (SCREENPOS(15, 0)) COLOR wallColor,"\191" 'right score devider PRINT AT (SCREENPOS(2, 0)) COLOR leftPaddleColor,"P1" 'P1 score label PRINT AT (SCREENPOS(16, 0)) COLOR rightPaddleColor,"P2" 'P2 score label PRINT AT (SCREENPOS(0, 0)),(scoreLeft+16)*8+6 'P1 score val PRINT AT (SCREENPOS(19, 0)),(scoreRight+16)*8+6 'P2 score val 'check to see if either player has won the game (score=9) IF (scoreLeft = 9) THEN PRINT AT (SCREENPOS(6, 6)) COLOR leftPaddleColor,"P1 Wins!" : PRINT AT (SCREENPOS(6, 0)) COLOR leftPaddleColor,"Game Over" : GOTO gameOver 'if LEFT wins we print stuff in left's color IF (scoreRight = 9) THEN PRINT AT (SCREENPOS(6, 6)) COLOR rightPaddleColor,"P2 Wins!" : PRINT AT (SCREENPOS(6, 0)) COLOR rightPaddleColor,"Game Over" : GOTO gameOver 'if RIGHT wins we print stuff in right's color 'draw left paddle initial state SPRITE 0,leftPaddleX+HIT+VISIBLE,leftPaddleY,SPR00 + leftPaddleSpriteColor SPRITE 1,leftPaddleX+HIT+VISIBLE,leftPaddleY+4+ZOOMY2,SPR01 + leftPaddleSpriteColor SPRITE 2,leftPaddleX+HIT+VISIBLE,leftPaddleY+12,SPR02 + leftPaddleSpriteColor 'draw right paddle initial state SPRITE 3,rightPaddleX+HIT+VISIBLE,rightPaddleY+FLIPX,SPR00 + rightPaddleSpriteColor SPRITE 4,rightPaddleX+HIT+VISIBLE,rightPaddleY+4+ZOOMY2+FLIPX,SPR01 + rightPaddleSpriteColor SPRITE 5,rightPaddleX+HIT+VISIBLE,rightPaddleY+12+FLIPX,SPR02 + rightPaddleSpriteColor 'draw ball initial state SPRITE 6,ballX+HIT+VISIBLE,ballY+ZOOMY2,SPR03 + ballColor 'begin waiting for next serve------------------------------------------------------- 'debounce controllers (thanks SDK documentation!) debounceCounter = 0 WHILE (debounceCounter < DEBOUNCE_DELAY) WAIT IF (CONT <> NO_KEY) THEN Counter = 0 ELSE debounceCounter = debounceCounter + 1 WEND 'controllers are both at rest 'now determine who should serve. Player who LOST the last point should serve IF (scoredAgainst = 1) THEN GOTO waitForP1Serve ELSE IF (scoredAgainst = 2) THEN GOTO waitForP2Serve waitForP1Serve: PRINT AT (SCREENPOS(6, 0)) COLOR leftPaddleColor,"P1 Serve" 'wait for keypress IF (CONT1.B0 OR CONT1.B1 OR CONT1.B2) THEN GOTO serve ELSE GOTO waitForP1Serve waitForP2Serve: PRINT AT (SCREENPOS(6, 0)) COLOR rightPaddleColor,"P2 Serve" 'wait for keypress IF (CONT2.B0 OR CONT2.B1 OR CONT2.B2) THEN GOTO serve ELSE GOTO waitForP2Serve 'we are no longer waiting for next serve-------------------------------------------- serve: 'SERVE the ball! PRINT AT (SCREENPOS(6, 0))," " 'clear "P# Serve" prompt from screen ballDirY=RAND(3) 'choose random Y-dir for the ball. We allow 0-2 which will represent up, flat, and down IF ballDirY=2 THEN ballDirY=-1 'if we rolled a 2, we convert it to -1 (up). 0 (flat) and 1 (down) will be left as-is. ballDirX=RAND(2) 'choose random X-dir for the ball. We allow 0-1 which will represent left and right. We can't have X-dir be 0 or else the ball will bounce straight up and down forever. If ballDirX=0 THEN ballDirX=-1 'if we rolled a 0, we convert it to -1 (left). 1 will be left as-is (right). loop: 'main loop WAIT 'WAIT to make sure things settle? 'draw left paddle SPRITE 0,leftPaddleX+HIT+VISIBLE,leftPaddleY,SPR00 + leftPaddleSpriteColor SPRITE 1,leftPaddleX+HIT+VISIBLE,leftPaddleY+4+ZOOMY2,SPR01 + leftPaddleSpriteColor SPRITE 2,leftPaddleX+HIT+VISIBLE,leftPaddleY+12,SPR02 + leftPaddleSpriteColor 'draw right paddle. Right paddle is x-flipped left paddle. SPRITE 3,rightPaddleX+HIT+VISIBLE,rightPaddleY+FLIPX,SPR00 + rightPaddleSpriteColor SPRITE 4,rightPaddleX+HIT+VISIBLE,rightPaddleY+4+ZOOMY2+FLIPX,SPR01 + rightPaddleSpriteColor SPRITE 5,rightPaddleX+HIT+VISIBLE,rightPaddleY+12+FLIPX,SPR02 + rightPaddleSpriteColor 'draw ball SPRITE 6,ballX+HIT+VISIBLE,ballY+ZOOMY2,SPR03 + ballColor 'this is where I'd like to animate the ball, (i.e. change to SPR04 for X frames, wait X frames, then change back to SPR03). 'left controller input IF CONT1.UP THEN IF leftPaddleY>18 THEN leftPaddleY=leftPaddleY-paddleSpeed IF CONT1.DOWN THEN IF leftPaddleY<86 THEN leftPaddleY=leftPaddleY+paddleSpeed 'right controller input IF CONT2.UP THEN IF rightPaddleY>18 THEN rightPaddleY=rightPaddleY-paddleSpeed IF CONT2.DOWN THEN IF rightPaddleY<86 THEN rightPaddleY=rightPaddleY+paddleSpeed 'wall bounce IF COL6 AND HIT_BACKGROUND THEN ballDirY=ballDirY*-1 :SOUND 1,900,15 : IF (ballY < 20) THEN ballY=19 : GOTO skipmove ELSE ballY=95 : GOTO skipMove 'always jump to skipMove when a collision so we dont jam the ball further inside 'left paddle collision 'if SPRITE6 hits SPRITE0, then we hit the Left Paddle Upper. Redirect the ball, play a sound, move the ball to 1px in front of the paddle, and change Y-dir if needed. etc. IF COL6 AND HIT_SPRITE0 THEN ballDirX=ballDirX*-1 : SOUND 1,500,15 : ballX=14 : IF (ballDirY = 0) THEN ballDirY=-1 : GOTO skipMove ELSE GOTO skipMove 'TODO more dynamic ball angles IF COL6 AND HIT_SPRITE1 THEN ballDirX=ballDirX*-1 : SOUND 1,500,15 : ballX=14 : GOTO skipMove IF COL6 AND HIT_SPRITE2 THEN ballDirX=ballDirX*-1 : SOUND 1,500,15 : ballX=14 : IF (ballDirY = 0) THEN ballDirY=1 : GOTO skipMove ELSE GOTO skipMove 'right paddle collision IF COL6 AND HIT_SPRITE3 THEN ballDirX=ballDirX*-1 : SOUND 1,500,15 : ballX=153 : IF (ballDirY = 0) THEN ballDirY=-1 : GOTO skipMove ELSE GOTO skipMove IF COL6 AND HIT_SPRITE4 THEN ballDirX=ballDirX*-1 : SOUND 1,500,15 : ballX=153 : GOTO skipMove IF COL6 AND HIT_SPRITE5 THEN ballDirX=ballDirX*-1 : SOUND 1,500,15 : ballX=153 : IF (ballDirY = 0) THEN ballDirY=1 : GOTO skipMove ELSE GOTO skipMove WAIT 'I really don't know when I should be using these WAITs. SOUND 1,0,0 'stop playing the collision sound. 'normalize ballSpeed 'This is needed because if x-dir and y-dir are both <> 0, then the ball is moving at 2 units per frame (1 in X and 1 in Y)... '...but if x- or y-dir are 0, the ball will only be moving 1 unit per frame. If that's the case, we "boost" the ball so it's always moving at 2 units per frame. IF (ballDirY = 0 AND ballBoost = 0) THEN\ ballBoost=ballSpeed\ ELSE IF (ballDirY <> 0) THEN\ ballBoost=0 'move ball IF (ballDirY < 0) THEN ballY=ballY-(ballSpeed+ballBoost) IF (ballDirY > 0) THEN ballY=ballY+(ballSpeed+ballBoost) IF (ballDirX < 0) THEN ballX=ballX-(ballSpeed+ballBoost) IF (ballDirX > 0) THEN ballX=ballX+(ballSpeed+ballBoost) skipMove: 'check for goal IF (ballX < 5) THEN\ scoreRight=scoreRight+1 : scoredAgainst=1 : GOTO nextPoint\ ELSE IF (ballX > 165) THEN\ scoreLeft=scoreLeft+1 : scoredAgainst=2 : GOTO nextPoint GOTO loop gameOver: 'clear the sprites from the field and hold here until someone RESETs the Intellivision' SPRITE 0,0,0 SPRITE 1,0,0 SPRITE 2,0,0 SPRITE 3,0,0 SPRITE 4,0,0 SPRITE 5,0,0 SPRITE 6,0,0 GOTO gameOver sprites: 'Paddle BITMAP ".##....." BITMAP "..#....." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "####...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "####...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..##...." BITMAP "..#....." BITMAP ".##....." 'Ball frame 1 BITMAP "..###..." BITMAP ".#.#.#.." BITMAP "#.#.#.#." BITMAP "##.#.##." BITMAP "#.#.#.#." BITMAP ".#.#.#.." BITMAP "..###..." BITMAP "........" BITMAP "........" 'Ball frame 2 BITMAP "..###..." BITMAP ".##.##.." BITMAP "##.#.##." BITMAP "#######." BITMAP "##.#.##." BITMAP ".##.##.." BITMAP "..###..." BITMAP "........" BITMAP "........"