;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Random Number Generator (WORD)

DoQuickRandomWord:
	push bc
	push de
		z_ld_bc_from RandomSeed
		inc bc
		z_ld_bc_to RandomSeed
		call DoRandomWord
	pop de
	pop bc
	ret
DoRandomWord:		;Return Random pair in HL from Seed
	call DoRandomByte1		;Get 1st byte
	push af
	push bc
		call DoRandomByte2	;Get 2nd byte
	pop bc
	pop hl
	ld l,a
	inc bc
	ret

DoRandomByte1:
	ld a,c			;Get 1st sed
DoRandomByte1b:
	rrca			;Rotate Right
	rrca
	xor c			;Xor 1st Seend
	rrca
	rrca			;Rotate Right
	xor b			;Xor 2nd Seed
	rrca			;Rotate Right
 	xor %10011101	;Xor Constant 
	xor c			;Xor 1st seed
	ret

DoRandomByte2:
	ld hl,Randoms1	
	ld a,b		
	xor %00001011
	and %00001111	;Convert 2nd seed low nibble to Loojup
	add l
	ld l,a
	ld d,(hl)		;Get Byte from LUT 1

	call DoRandomByte1	
	and %00001111	;Convert random number from 1st geneerator to Lookup
	ld hl,Randoms2
	add l
	ld l,a
	ld a,(hl)		;Get Byte from LUT2
	xor d			;Xor 1st lookup
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;DoRandom Byte	
	
DoRandom:		;RND outputs to A (no input)
	push hl
	push bc
	push de
		z_ld_bc_from RandomSeed	;Get and update Random Seed
		inc bc
		z_ld_bc_to RandomSeed
		call DoRandomWord
		ld a,l
		xor h
	pop de
	pop bc
	pop hl
	ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Ranged Ramdom

DoRangedRandomAgain:	
	pop hl
DoRangedRandom:	;return HL value between BC and DE (using Mask IX)
	push bc
		push hl
		call DoQuickRandomWord	;Get a 16 bit value
		pop bc
		ld a,h
		xor b
		z_and_ixh ;and ixh				;Mask H Byte
		ld h,a
		ld a,l
		z_and_ixl ;and ixl				;Mask L Byte
		xor c	
		ld l,a
	pop bc
	push hl
		or a
		z_sbc_hl_bc ;sbc hl,bc			;Check if<BC
		jr c,DoRangedRandomAgain
	pop hl	
	push hl
		or a
		z_sbc_hl_de ;sbc hl,de			;Check if>DE
		jr nc,DoRangedRandomAgain
	pop hl
	ret
	

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DoXLineObj:		;Object on horizontal plane
	ld bc,8					
	ld de,ScreenWidth-8			;Full Vertical Range
	
	z_ld_ix &1FF					;Ramdom mask
	call DoRangedRandom			;return value between BC and DE

	push hl
	ifdef ScreenWidth20
		ld bc,0+32
		ld de,ScreenHeight-32	;Narrow Horizontal Range
	else
		ld bc,0+64
		ld de,ScreenHeight-64	;Narrow Horizontal Range
	endif
		call DoRangedRandom		;return value between BC and DE
		z_ex_dehl ;ex de,hl
	pop hl
	call Locate
	ret

	
DoYLineObj:		;Object on vertical plane
	ifdef ScreenWidth20
		ld bc,0+32
		ld de,ScreenWidth-32	;Narrow Vertical Range
	else
		ld bc,0+64
		ld de,ScreenWidth-64	;Narrow Vertical Range
	endif
	z_ld_ix &1FF					;Ramdom mask
	call DoRangedRandom			;return value between BC and DE

	push hl
		ld bc,8
		ld de,ScreenHeight-8	;Full Horizontal Range
		
		call DoRangedRandom		;return value between BC and DE
		z_ex_dehl ;ex de,hl
	pop hl
	call Locate
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MainMenu:
	
	ld a,1					;Reset Game Settings
	ld (Level),a
	ld a,4
	ld (Lives),a

	call Cls 
	call DoTitleScreen		;Show Title
	call WaitForFire
Restart:
	call LevelInit			;Setup Level
	jp infloop
	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
	

DoTitleScreen:

;Draw Title Graphics

	ld hl,ScreenWidth/2		;Screen Center
	ld de,ScreenHeight/2
	call Locate
	ifdef ScreenWidth20
		ld a,0
	else
		ld a,1
	endif
 	ld (scale),a		;Set Scale

	ld a,Color2
	ld (LineColor),a

	ld hl,VecTitleB		;Title 3D depth
	call drawpacket

	ld hl,VecBall		;Ball Torso
	call drawpacket
	
	ld hl,VecHands		;Ball Hands
	call drawpacket

	ld hl,VecEyes		;Ball Eyes
	call drawpacket

	ld hl,VecMouth		;Ball Mouth
	call drawpacket

	ld a,Color3
	ld (LineColor),a

	ld hl,VecTitleF		;Title Main
	call drawpacket

	ld a,Color1
	ld (LineColor),a
	
	ld hl,VecToung		;Ball Toung
	call drawpacket

	ifdef ScreenWidth20
		ld a,1
	else
		ld a,2
	endif
 	ld (scale),a

	z_ld_ix VecTitleZoom	;Speed lines of ball
	call drawcpacket

	ld a,Color3
	ld (LineColor),a
	
	z_ld_ix VecTitleWall	;Wall
	call drawcpacket

	
;Draw Title Text
	ld a,-1
 	ld (scale),a
	ld a,Color4
	ld (LineColor),a
	
	ifdef ScreenWidth20	;Title split on small screen
		ld hl,24
		ld de,8
		call Locate
		ld hl,Ttitle1
		call PrintString
		ld hl,32
		ld de,16
		call Locate
		ld hl,Ttitle2
		call PrintString
	else
		ifdef ScreenWidth32
			ld hl,15
		else
			ld hl,35
		endif
		ld de,55
		call Locate
		ld hl,Ttitle	;Title message
		call PrintString
	endif
	
	ld a,Color1
	ld (LineColor),a
	ld a,-1
 	ld (scale),a

	ld hl,30
	ld de,ScreenHeight-16
	call Locate

	ld hl,TBestLevel
	call PrintString	;'High score'

	ld a,(BestLevel)
	call ShowDecimal
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Main Level Loop

LevelInit:
	ld a,(level)			;Check current level
	and %00000111
	cp %00000111
	jr nz,NoExtraLife
	ld a,(Lives)	
	inc a					;Extra life every 8 levels
	ld (Lives),a
NoExtraLife:

	ifdef ScreenWidth20
		ld a,49*2+1
	else
		ld a,99*2+1			;Recharge boost power
	endif
	ld (BoostPower),a

	ld a,(Level)			;Calculate CPU AI
	srl a
	srl a					;/4
	ld b,a
	ld a,7					;Stupid=7 Smart=0
	sub b
	jr nc,CpuAiOK
	xor a
CpuAiOK:
	ld (CpuAI),a

	ld hl,UserRamBak	;Reset level settings 
	ld de,UserRam
	ld bc,UserRamBakEnd-UserRamBak
	z_ldir

;Draw new Level Objects
	
	call CLS 				;Clear Screen

	ld a,(Level)			;Calculate no of pairs of objects
	add 4					;4 pair min
	z_ld_ixh_a ;ld ixh,a
	z_ld_ixl 10				;Max 10 pairs of Square objects
	
	ifdef ScreenWidth20
		ld a,0
	else
		ld a,1
	endif
 	ld (scale),a

	ld a,Color4
	ld (LineColor),a
MoreObject1:
	z_push_ix ;push ix
		call DoXLineObj
		z_ld_ix Object1		;Square object H
		call drawcpacket

		call DoYLineObj 
		z_ld_ix Object1		;Square object V
		call drawcpacket
	z_pop_ix ;pop ix
	z_dec_ixl ;dec ixl
	jr z,DoObject2			;10 obj pair limit for squares
	z_dec_ixh ;dec ixh
	jr nz,MoreObject1		;Decrease obj count
	
DoObject2:
	z_ld_ixh_a ;ld a,ixh				;Remainder are hollow objects
	or a
	jr z,ObjectsDone
MoreObject2:
	z_push_ix ;push ix
		call DoXLineObj
		z_ld_ix Object2		;Hollow object H
		call drawcpacket

		call DoYLineObj
		z_ld_ix Object2		;Hollow object V
		call drawcpacket
	z_pop_ix ;pop ix
	z_dec_ixh ;dec ixh
	jr nz,MoreObject2
ObjectsDone:
	ld a,Color1
	ld (LineColor),a
	
	
;Draw Screen borders

	ld de,ScreenWidth-1
HlineAgain:
	ld hl,0
	ld a,Color1
	call PsetHLDE;	;DE=Xpos HL=Ypos A=Color
	ld hl,ScreenHeight-1	
	ld a,Color1
	call PsetHLDE;	;DE=Xpos HL=Ypos A=Color
	dec de
	ld a,d 
	cp 255
	jr nz,HlineAgain

	ld hl,ScreenHeight-1
VlineAgain:
	ld de,0
	ld a,Color1
	call PsetHLDE;	;DE=Xpos HL=Ypos A=Color
	ld de,ScreenWidth-1
	ld a,Color1
	call PsetHLDE;	;DE=Xpos HL=Ypos A=Color
	dec hl
	ld a,h 
	or l
	jr nz,VlineAgain

;Draw text 	
	ifdef ScreenWidth20
		ld a,-1 
	else
		ld a,0
	endif
	ld (scale),a

	ld hl,-4
	ifdef ScreenWidth20
		ld de,4
	else 
		ld de,10
	endif 
	call Locate
	ld a,(Lives)				;Lives = TopLeft
	call ShowDecimal
	
	ifdef ScreenWidth20
		ld hl,ScreenWidth-19
		ld de,ScreenHeight-8
	else
		ld hl,ScreenWidth-38
		ld de,ScreenHeight-16
	endif
	call Locate
	ld a,(Level)				;Level= BottomRight
	call ShowDecimal
	
	ifdef ScreenWidth20
		ld hl,ScreenWidth-19
	else
		ld hl,ScreenWidth-38
	endif
	ifdef ScreenWidth20
		ld de,4
	else 
		ld de,10
	endif 
	call Locate
	ld a,(CpuAi)				;AI=TopRight
	call ShowDecimal
	
	ld a,(boostpower)
	call ShowBoostPower			;Boost=BottomLeft
	
;Draw Corners around text

	ifdef ScreenWidth20
		z_ld_ix 0
		z_ld_iy 12
		ld hl,16
		ld de,12
	else
		z_ld_ix 0
		z_ld_iy 24
		ld hl,32
		ld de,24
	endif
	call DrawLine	;Start=IX,IY... Dest hl,DE=Yoffset 
	ld hl,0
	ifdef ScreenWidth20
		ld de,-12
	else
		ld de,-24
	endif
	call DrawLineRelative
	ifdef ScreenWidth20
		z_ld_ix ScreenWidth-17
		z_ld_iy ScreenHeight-1
		ld hl,ScreenWidth-17
		ld de,ScreenHeight-13
	else
		z_ld_ix ScreenWidth-33
		z_ld_iy ScreenHeight-1
		ld hl,ScreenWidth-33
		ld de,ScreenHeight-25
	endif
	call DrawLine	;Start=IX,IY... Dest hl,DE=Yoffset 
	ifdef ScreenWidth20
		ld hl,16
	else
		ld hl,32
	endif
	ld de,0
	call DrawLineRelative

	ifdef ScreenWidth20
		z_ld_ix 16
		z_ld_iy ScreenHeight-1
		ld hl,16
		ld de,ScreenHeight-13
	else
		z_ld_ix 32
		z_ld_iy ScreenHeight-1
		ld hl,32
		ld de,ScreenHeight-25
	endif
	call DrawLine	;Start=IX,IY... Dest hl,DE=Yoffset 
	ifdef ScreenWidth20
		ld hl,-16
	else
		ld hl,-32	
	endif
	ld de,0
	call DrawLineRelative
	ifdef ScreenWidth20
		z_ld_ix ScreenWidth-17
		z_ld_iy 0
		ld hl,ScreenWidth-17
		ld de,12
	else
		z_ld_ix ScreenWidth-33
		z_ld_iy 0
		ld hl,ScreenWidth-33
		ld de,24	
	endif
	call DrawLine	;Start=IX,IY... Dest hl,DE=Yoffset 
	ifdef ScreenWidth20
		ld hl,16
	else
		ld hl,32
	endif
	ld de,0
	call DrawLineRelative
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ShowBoostPower:
	push af
		ld hl,-4
		ifdef ScreenWidth20
			ld de,ScreenHeight-6
		else
			ld de,ScreenHeight-16
		endif
		call Locate
	pop af
	srl a 						;Halve boost
	call ShowDecimal			;Show number to screen
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GameOver:
	call Cls
	ifdef ScreenWidth20
		ld a,0
	else
		ld a,1
	endif
 	ld (scale),a				;Text Scale
	ld a,Color1					
	ld (LineColor),a			;Color
	
	ifdef ScreenWidth40
		ld hl,50				;Xpos
	else
		ld hl,20
	endif
	ld de,ScreenHeight/2-20		;Ypos
	call Locate

	ld hl,tGameOver
	call PrintString			;Show Gameover Message
	ifdef ScreenWidth20
		ld a,-1
	else
		ld a,0
	endif
 	ld (scale),a

	ld a,(level)
	ld hl,bestlevel				;Beat best level?
	cp (hl)
	jr c,MajorSuckage
	jr z,MajorSuckage
NewBest:
	ld (hl),a					;Yes? Update BEST!
	ld a,Color3
	ld (LineColor),a

	ifdef ScreenWidth40
		ld hl,40				;Xpos
	else
		ld hl,10
	endif
	ld de,ScreenHeight/2+20		;Ypos
	call Locate

	ld hl,TYouRock				;New Highscore message
	call PrintString
	jr WaitRestart
MajorSuckage:
	ld a,Color2
	ld (LineColor),a

	ifdef ScreenWidth40
		ld hl,0
	else
		ld hl,20
	endif
	ld de,ScreenHeight/2+20
	call Locate
	
	ld hl,TYouSuck				;No Highscore message
	call PrintString

WaitRestart:
	call WaitForFire
	jp MainMenu					;New Game

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

HandlePlayer:
	ld a,(boost)			;Player using boost?
	or a
	jr nz,NoBoost
	
	ld a,(boostpower)
	and %11111110			;We only show BoostPower/2 
	ld e,a

	ld a,(ShownBoostPower)
	and %11111110
	cp e
	jr z,BoostPowerSame

	ld a,0
	ld (linecolor),a	
	ld a,(ShownBoostPower)
	call ShowBoostPower		;Clear old boost power

	ld a,(BoostPower)		;Decrease boost power
	dec a
	ld (BoostPower),a

	ld a,Color1
	ld (linecolor),a
	
	ld a,(boostpower)
	ld (ShownBoostPower),a
	call ShowBoostPower		;Show new boost power
	
	jr NoBoost
	
BoostPowerSame:
	ld a,(BoostPower)		;Decrease boost power
	dec a
	ld (BoostPower),a

NoBoost:
	ld hl,boost				;No Boost=Move every other tick
	ld a,(tick)
	and (hl)				;Boost=Move every tick
	cp 0
	jr nz,NotPlayerTick

	z_ld_bc_from PlayerXacc
	z_ld_hl_from PlayerX			;Move Player X
	add hl,bc
	z_ld_hl_to PlayerX
	z_ex_dehl ;ex de,hl

	z_ld_bc_from PlayerYacc
	z_ld_hl_from PlayerY			;Move Player Y
	add hl,bc
	z_ld_hl_to PlayerY
	
	ld a,d	;XH
	ld b,e	;XL
	ld c,l	;YL

	push af
	push bc
		call Point			;Has player collided?
		or a
		jp z,NotHit
	
		ld hl,Lives			;Yes! Depleat lives
		dec (hl)

		jp nz,Restart
		jp GameOver			;No lives left - player dead

NotHit:		
	pop bc
	pop af
	ld d,Color2
	call Pset				;Draw player to screen

NotPlayerTick:
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SetPlayerDirection:			;Rotate player in response to keypress
	push hl
	z_push_ix ;push ix
	push bc
	push de
		ld a,(hl)
		and %00000011		;Wrap around 4 directions
		ld (hl),a
		sla a				;4 bytes per direction
		sla a
		ld c,a
		ld b,0
		ld hl,Directions
		add hl,bc

		ld bc,4				;Copy 4 bytes to Player/Cpu acceleration
		z_push_ix ;push ix
		pop de
		z_ldir 
	pop de
	pop bc
	z_pop_ix ;pop ix
	pop hl
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


HandleCPU:						;Computer playrt	
	ld a,(Tick)
	cp 1
	jp nz,NoCpuTick				;Move every other other tick

	z_ld_bc_from CpuXacc
	z_ld_hl_from CpuX
	add hl,bc					;Move X
	call ApplyAI				;Look ahead for CPU responses
	
	push hl
	z_pop_ix ;pop ix						;Store Xpos for later
	z_ex_dehl ;ex de,hl
	
	z_ld_bc_from CpuYacc
	z_ld_hl_from CpuY
	add hl,bc					;Move Y
	call ApplyAI				;Look ahead for CPU responses
	
	push hl
	z_pop_iy ;pop iy						;Store Ypos for later
	
		
	ld a,d	;XH
	ld b,e	;XL
	ld c,l	;YL
	call Point	;X=AB Y=C		;Test Cpu Pos (For AI)
	or a
	jp z,NotMoveCPU				;Cpu not about to collide

	call dorandom				;Change CPU turn direction?
	cp 240
	jr c,CpuDirectionOK
	ld a,(CpuTurn)
	z_neg							;Flip Rotation Direction
	ld (CpuTurn),a
	
CpuDirectionOK:
	ld b,3						;Find a direction we can turn.
NextCpuTest:
	ld a,(CpuDirection)
	ld hl,CpuTurn
	add (hl)					;Rotate once
	and %00000011				
	ld (CpuDirection),a

	ld a,b
	cp 2
	jp z,TestSkip 				;Facing Opposited direction?
	push bc
	ld a,(CpuDirection)
		sla a
		sla a
		ld c,a
		ld b,0
		z_push_ix ;push ix
			z_ld_ix Directions	;Get CPU Direction
			z_add_ix_bc ;add ix,bc
			z_ld_a_ix_plusn 2
			ld l,a
			z_ld_a_ix_plusn 3
			ld h,a
			z_ld_e_ix_plusn 0
			z_ld_d_ix_plusn 1
		z_pop_ix ;pop ix
		call CpuTest ;HL=Yacc ;De=Xacc - Test Direction
	pop bc
	jr nc,FoundCpuMove			;Found A Move
TestSkip:
	z_djnz NextCpuTest			;Repeat Check

FoundCpuMove:
NotMoveCPU:
	z_ld_bc_from CpuXacc
	z_ld_hl_from CpuX				;Update up X
	add hl,bc
	z_ld_hl_to CpuX
	z_ex_dehl ;ex de,hl

	z_ld_bc_from CpuYacc
	z_ld_hl_from CpuY				;Update up Y
	add hl,bc
	z_ld_hl_to CpuY

	ld a,d	;XH
	ld b,e	;XL
	ld c,l	;YL
	push af
	push bc
		call Point				;Check CPU Position
		or a
		jp z,NotHitCPU
		ld hl,Level				;Cpu Dead - next level!
		inc (hl)
		jp Restart
NotHitCPU:
	pop bc
	pop af
	ld d,Color3
	call Pset					;Draw Cpu to screen
NoCpuTick:
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ApplyAI:					;Look ahead level
	ld a,(CpuAI)			;lower=tighter reactions (Better)
	or a
	ret z	
ApplyAIAgain:
	add hl,bc				;Shift CPU Check pixel by 1 unit
	dec a
	jr nz,ApplyAIAgain
	ret	

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

CpuTest:	;HL=Yacc ;De=Xacc	- Test a possible turn
	z_ld_hl_to CpuYacc
	z_ld_de_to CpuXacc	;Store the tested Rotation
	push hl
	push de
		z_ld_b_ixh ;ld b,ixh	
		z_ld_c_ixl ;ld c,ixl
		add hl,bc		;Add X-Offset
		z_ex_dehl ;ex de,hl
		
		z_ld_b_iyh ;ld b,iyh	
		z_ld_c_iyl ;ld c,iyl
		add hl,bc		;Add Y-Offset
		
		ld a,d	;XH
		ld b,e	;XL
		ld c,l	;YL
		call Point		;Test point
	pop de
	pop hl
	or a
	ret z			  	;NoCollide (NC)
	scf 				;Collide (C)
	ret

