; PONG game using XB and MLC with Precompiler
; 2011 guillaume.tello@orange.fr

100 CALL CLEAR::DIM S$(3)
; load compiler and compiles game and sounds

110 GOSUB 1000

; ball, paddle, net and field definitions

140 CALL CHAR(96,"60F0F0F0F0F0F060")::CALL CHAR(100,"60F0F06000000000")
150 CALL CHAR(97,"8855225588552255")::CALL CHAR(98,"FFFFFFFFFFFFFFFF")::CALL CHAR(99,"0000000000000000")

; prepares screen

160 CALL CLEAR::CALL SCREEN(1)::CALL COLOR(9,16,10)
170 FOR I=1 TO 8::CALL COLOR(I,4,1)::NEXT I
180 RESTORE 900::READ M$,N$,P$::READ SP(1),SP(2),SP(3),SP(4)
190 DISPLAY AT(1,13):"PONG"
200 DISPLAY AT(2,1):M$
210 FOR I=3 TO 19::DISPLAY AT(I,1):N$::NEXT I
220 DISPLAY AT(11,1):P$::DISPLAY AT(20,1):M$
230 DISPLAY AT(21,2):"LEFT (X-E)     RIGHT (I-M)"
240 SC(1)=0::SC(2)=0::START=1::SPEED=2::CALL MAGNIFY(2)

; display little menu and wait for SPACE to start

250 DISPLAY AT(24,1):"SPACE=START  S=SPEED  Q=QUIT"
260 DISPLAY AT(22,7):SC(1)::DISPLAY AT(22,21):SC(2)::GOSUB 400::IF K$<>" " THEN 250

; sprites 2 and 3 are the paddles, sprite 1 the ball

270 CALL SPRITE(#2,96,5,16,24)::CALL SPRITE(#3,96,14,136,224)
280 CALL SPRITE(#1,100,13,120*START-102,184*START-152)

; call assembly routine to play

290 CALL LINK("PLAY",S$(),START,SP(SPEED),WIN)

; upon return, WIN is the winner!

300 SC(WIN)=SC(WIN)+1:: START=3-START::GOTO 260

; quit game, the assembly routine is deleted from ram

310 CALL LINK("POP",A,B)::PRINT A,B
320 END

; menu key

400 CALL KEY(0,K,S)::K$=CHR$(ABS(K))::IF K$=" " THEN RETURN
410 IF K$="S" OR K$="s" THEN 420
415 IF K$="Q" OR K$="q" THEN 310 ELSE 400
420 DISPLAY AT(24,1):"SELECT SPEED FROM 1 TO 4:";SPEED
430 ACCEPT AT(24,27)SIZE(-1)BEEP:SPEED
440 RETURN

; field definition

900 DATA "bbbbbbbbbbbbbaabbbbbbbbbbbbb"
910 DATA "bccccccbcccccaacccccbccccccb"
920 DATA "bccccccbbbbbbaabbbbbbccccccb"

; speed table 1 to 4

930 DATA 10,20,35,50

; includes here the loader from line 1000 and DATA from line 2000
; --> to load the compiler (normal mode) use $MLC N ...
; --> to use the Fast Loader use $MLC F ...
; --> If compiler in memory and you don't want it to be loaded, remove CALL INIT and use $MLC D ...

$MLC F 1000 10 2000
1900 RETURN

; sound definitions

$SND 1	; ball touches paddle 1
	FA440VA0VN15D2 VA2D3 VA4D4 VA6D5 VA8D6,VA12D7 VA14D8 VA15D0
$$
$SND 2	; ball touches paddle 2
	FA220VA0VN15D2 VA2D3 VA4D4 VA6D5 VA8D6,VA12D7 VA14D8 VA15D0
$$
$SND 3	; ball touches border
	FN5VN8VA15D1 VN6D1 VN4D1 VN6D1 VN8D2,VN12D2 VN15D0
$$

; game routine

$PLAY
	GETPARAM 2 S				; S=start player (1/2)
	GETPARAM 3 H				; horizontal speed
	RND					; random number in Z
	DIV Z H					; reminder (so Z<H)
	LET G Z					; vertical speed!
	LET M 1					; default player 1
	COMPARE S 1		
	IF<>					; if start player is not 1
		NEG G				; modifies motion and M=2
		NEG H
		INC M
	ENDIF
	SMAX 1					; one sprite with auto motion
	SMOTION 1 G H				; ball starts !
	SOUND M					; with a paddle sound
	SPOSITION 2 A B				; get positions of both paddles
	SPOSITION 3 C D
	DO
		INTERRUPT			; enables interrupt for auto motion

		; here player ONE

		KEY 1				; read keyboard left, key in K and COMPARE K 0 performed
		IF>=				; a key pressed!
			IF=			; if equal 0, it is X
				INC A		; here "X"=down, A=A+1
			ELSE
				COMPARE K 5	; is it "E"?
				IF=
					DEC A	; here "E"=up, A=A-1
				ENDIF
			ENDIF
			LIMIT 16 136 A		; ensure A is in the range
			SLOCATE 2 A B		; and set new paddle position
		ENDIF

		COMPARE K 18			; is it "Q"
	WHILE<>					; if so QUIT !

		GOSUB b				; manages ball movement

		; here player TWO

		KEY 2				; read keyboard right, key in K and COMPARE K 0 performed
		IF>=				; a key pressed!
			IF=			; if 0, it is "M"
				INC C		; if "M"=down, C=C+1
			ELSE
				COMPARE K 5	; is it "I"?
				IF=
					DEC C	; if "I"=up, C=C+1
				ENDIF
			ENDIF
			LIMIT 16 136 C		; ensure C is in the range
			SLOCATE 3 C D		; new position
		ENDIF

		GOSUB b				; manages ball movement

	LOOP					; and back to paddle one !!!

	GOTO x					; here if K=18, "Q" key, QUIT

; Subroutine for ball movement

SLABEL b
	SPOSITION 1 E F			; get ball position
	LIMIT 16 144 E			; is the vertical position in the field?
	IF<>				; no,so modifications!
		NEG G			; reverse motion
		SMOTION 1 G H		; reflexion
		SLOCATE 1 E F		; new location
		SOUND 3			; and border sound
	ENDIF

	LIMIT 24 224 F			; is the horizontal potition in the field?
	IF<>THEN x			; if not, game has ended!

	LIMIT 32 216 F			; else, are we far from the paddles?
	IF<>				; not so far, verify contact
		IF<			; if under 32 then work with paddle 1
			LET G A		; take vertical position of...
			LET M 1		; ...paddle 1
		ELSE
			LET G C		; else take vertical position of...
			LEt M 2		; ...paddle 2
		ENDIF
		SUB G E			; vertical distance G-E
		LIMIT -16 8 G		; is it in -16,8 ?
		IF=			; yes so, contact!
			ADD G 4		; ball touches the paddle M
			ADD G G		; G=2*(vertical distance+4) new vertical speed
			NEG G		; reflexion
			NEG H		; idem
			SMOTION 1 G H	; new ball motion
			SLOCATE 1 E F	; new location
			SOUND M		; and sound for paddle contact
		ENDIF
	ENDIF
	RETURN				; back to players keys

; end of game

LABEL x
	SMAX 0			; end of game, stop every sprite
	LET R 1			; default winner
	COMPARE F 124
	IF<			; if position under 124, winner is 2
		INC R		; R=2
	ENDIF
	PUTPARAM 4 R		; return winner
$$
$END