
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;						Lesson YQuest2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	
; Random Number Generator


	
DoRandomWord:		;Return Random pair in HL from Seed BC
	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:		;RND outputs to A (no input)
	push hl
	push bc
	push de
		ld bc,(RandomSeed)	;Get and update Random Seed
		inc bc
		ld (RandomSeed),bc
		call DoRandomWord
		ld a,l
		xor h
	pop de
	pop bc
	pop hl
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DoRangedRandom: 		;Return a value between B and C
	call DoRandom
	cp B
	jr c,DoRangedRandom
	cp C
	jr nc,DoRangedRandom
	ret

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

RandomXpos:					;Pick a random horizontal location
	ld b,&02			;Min
	ld c,ScreenObjWidth	;Max
	call DoRangedRandom	
	ld (IX+O_Xpos),a
	ret
	
RandomYpos:					;Pick a random Vertical Position
	ld b,&08			;Min
	ld c,ScreenObjHeight;Max
	call DoRangedRandom
	ld (IX+O_Ypos),a
	ret

RandomizeObjectPosition:	;Randomize Location of object (both)
	push bc
		Call RandomXpos
		Call RandomYpos
	pop bc
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
RandomizeEdgeObjectPosition:	;Put an enemy at the edge of the screen
	push bc
		call dorandom
		and %00000011			;Pick one of the 4 sides
		jr z,RandomTop
		cp 1
		jr z,RandomBottom
		cp 2
		jr z,RandomLeft
		cp 3
		jr z,RandomRight
RandomTop:
		call RandomXpos
		ld (IX+O_Ypos),8		;Put enemy at top of screen
		jr RandomizeEdge
RandomBottom:
		call RandomXpos
		ld (IX+O_Ypos),ScreenObjHeight-8
		jr RandomizeEdge		;Put enemy at bottom of screen
RandomLeft:
		call RandomYpos
		ld (IX+O_Xpos),4
		jr RandomizeEdge		;Put Enemy on left of screen
RandomRight:
		call RandomYpos
		ld (IX+O_Xpos),ScreenObjWidth-6
RandomizeEdge:					;Put enemy on right of screen
	pop bc
	ret
	
	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Randomly Position an object IX without colliding with any other objects 
;Test C objects
SafeRandomizeObjectPosition:	
	push iy					
NextObjectInitReRandomize:
	push bc
		call RandomizeObjectPosition	;Select a new position
		ld iy,ObjectArray		;We need to check if an object is colliding
NextObjectInitTextNext:
		ld a,ixh				;Don't compare object to itself!
		cp iyh
		jr nz,CheckThisObject		
		ld a,ixl
		cp iyl
		jr nz,CheckThisObject
		jp CheckNextObject		;Comparing an object to itself!
CheckThisObject:
		push bc
			ld d,(IX+O_Xpos)
			ld e,(IX+O_Ypos)
			
			ld b,(IY+O_Xpos)
			ld c,(IY+O_Ypos)
			call RangeTestW	;Return C if collided limit 
		pop bc
		jr c,NextObjectInitReRandomize2	;Object overlaps existing object
CheckNextObject:
		ld de,ObjectByteSize
		add iy,de
		dec c
		jr nz,NextObjectInitTextNext
	pop bc
	pop iy
	ret
NextObjectInitReRandomize2:
	pop bc					;Reset enemy count and restart
	jr NextObjectInitReRandomize
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RangeTestW:	;Object Range Check
	push hl
	ld h,5
	ld l,10
	jr RangeTest2
RangeTest:	;Bullet Range check
	push hl
	ld h,3
	ld l,6
	
RangeTest2:		;See if object XY pos DE hits object BC (with radius HL)
	push bc
		ld a,b							;X axis check
		sub h				
		jr c,RangeTestB
		cp d
		jr nc,RangeTestOutOfRange
RangeTestB:
		add h
		add h
		jr c,RangeTestD
		cp d
		jr c,RangeTestOutOfRange
RangeTestD:	
		ld a,c							;Y Axis Check
		sub l
		jr c,RangeTestC
		cp e
		jr nc,RangeTestOutOfRange
RangeTestC:
		add l
		add l
		jr c,RangeTestE
		cp e
		jr c,RangeTestOutOfRange
RangeTestE:
		SCF 		;Collided = Carry
	pop bc
	pop hl
	ret
	
RangeTestOutOfRange:
		or a		;OK = Clear Carry
	pop bc
	pop hl
	ret

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

PrintSpace:
	ld a,' '
PrintChar:		;Show character to the screen with sprite font
	push bc
	push de
	push hl
	push af
		call PrintCharB			;this is platform specific
		ld hl,CursorX
		inc (hl)				;Increase Xpos
	pop af
	pop hl
	pop de
	pop bc
	ret

locate:
	ld (CursorY),hl				;Load X and Y
	ret

PrintString:
	ld a,(hl)					;Print a '255' terminated string 
	cp 255
	ret z
	inc hl
	call PrintChar
	jr PrintString	


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;						Lesson YQuest3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
DrawUI:
	ld a,(SpriteFrame)		;4 frames of animation!
	inc a
	and %00000011
	ld (SpriteFrame),a

	ld hl,&0000
	call Locate
	ld hl,TxtLives	
	call PrintString		;Show Lives
	ld a,(lives)
	add '0'
	call PrintChar

	ifdef ScreenWidth20
		ld h,ScreenWidth-5
	else
		ld h,ScreenWidth-12
	endif
	ld l,0
	
	call Locate
	ld hl,TxtCrystals	
	call PrintString		;Show Crystals

	ld a,(Crystals)
   	call ShowDecimal

	ifdef ScreenWidth20
		ld h,ScreenWidth-5
	else
		ld h,ScreenWidth-10
	endif
	ld l,ScreenHeight-1
	
	call Locate
	ld hl,TxtLevel
	call PrintString		;Show Level
	ld a,(Level)
	inc a
   	call ShowDecimal

	ld h,&00
	ld l,ScreenHeight-1
	call Locate
  	ld hl,TxtScore
   	call PrintString		;Show Score
   	ld de,Score		
   	ld b,4
   	call BCD_Show
    ret

	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
LevelInit:				;Define the new level data
	ld hl,LevelMap
	ld b,0
	ld a,(Level)		
	and %00001111		;Levels cycle after 16 levels
	rlca 				;2 bytes per pointer
	ld c,a
	add hl,bc

	ld a,(hl)			;Load levelmap address
	ld iyl,a
	inc hl
	ld a,(hl)
	ld iyh,a

	ld a,(IY)
	ld (Crystals),a		;Get crystal count from levelmap
	inc IY
	inc IY
	
	ld b,Enemies		;Populate enemies
	ld ix,ObjectArray
NextObjectType:	
	push iy
		ld d,(IY+1)		;Enemy Count
		inc d
		push de
			ld a,(IY+0)	;Enemy Number
			rlca 
			rlca
			rlca		;8 bytes per object def
			ld d,0
			ld e,a
			ld iy,EnemyDefinitions
			add iy,de	;Get Enemy Definition offset
		pop de
		
NextObjectInitLoop:	
		dec d
		jp z,LastObject			;Last object of this type?
		push de
		push bc		
			ld a,(IY+D_SprNum)	;Fill settings from enemy def
			ld (IX+O_SprNum),a
			ld a,(IY+D_CollProg)
			ld (IX+O_CollProg),a
			ld a,(IY+D_Program)
			ld (IX+O_Program),a
			ld a,(IY+D_Xacc)
			ld (IX+O_Xacc),a
			ld a,(IY+D_Yacc)
			ld (IX+O_Yacc),a

			push iy
				call RandomizeObjectPosition	;Position object
				ld a,enemies
				sub b
				jr z,TestNextObPosOK			;Don't check 1st object
				ld c,a
				;Check if object collides with existing
				call SafeRandomizeObjectPosition	
TestNextObPosOK:
				ld bc,ObjectByteSize
				add ix,bc		;Move to next object
			pop iy
		pop bc
		pop de
		dec b
		jr nz, NextObjectInitLoop	;Do next object
	pop iy
	
	ld hl,BulletArray		;Clear bullet array
	ld de,BulletArray+1
	ld (hl),255				;Unused bullet
	ld bc,BulletCount*ObjectByteSize*2-1 ;BulletArray+EnemyBulletArray
	ldir
	ret

LastObject:
	pop iy
	inc iy
	inc iy
	jp NextObjectType

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

ResetPlayer:
	ld a,48						;Set Invincibility Time
	ld (Invincibility),a

	xor a						;Silence sound
	ld (PlayingSFX),a
	
	ld hl,PlayerObjectBak		;Default player state
	ld de,PlayerObject			;Current player state
	ld bc,ObjectByteSize	
	ldir						;Reset player params
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DrawPlayer:
	ld IX,PlayerObject
	ld a,(Invincibility)		;Flash player if invincible
	or a
	jr z,InvOk
	dec a
	ld (Invincibility),a
InvOk:
	and %00000010
	call z,DrawObject			;Draw Player Sprite
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
DrawAndMove:					;Handle Player and enemies
	ld b,BulletCount			
	ld ix,BulletArray			;Do Player Bullets
NextBulletDrawLoop:
	push bc
		call BlankSprite			
		call DrawObject			
		ld bc,ObjectByteSize
		add ix,bc
	pop bc
	djnz NextBulletDrawLoop

	call DrawPlayer				;Show Player
	
;Do Player Bullets
	ld b,BulletCount
	ld ix,BulletArray
NextBulletLoop:
	push bc	
	ld a,(IX+O_CollProg)
	cp 250
	jr nc,EnemyNoTest	;Bullet left screen?

	
		ld c,enemies
		;Collision detection of bullet and enemy
		ld iy,ObjectArray 
EnemyCollideLoop:
		push bc
			ld a,(IY+O_CollProg)
			cp 1
			jr z,EnemyNoCollide		;Shot Crystal
			cp 250
			jr nc,EnemyNoCollide	;Shot Dead/uninitialized object

			ld a,(IY+O_SprNum)
			cp 4
			jr z,EnemyNoCollide		;Shot Mine
	
			ld d,(IX+O_Xpos)		;Bullet XY
			ld e,(IX+O_Ypos)	
			ld b,(IY+O_Xpos)		;Enemy XY
			ld c,(IY+O_Ypos)
			call RangeTest			;Return C if collided limit
			jr nc,EnemyNoCollide
			
			ld hl,BCD5
			call ApplyScore			;Player has shot enemy

			push ix
				push iy
				pop ix
				call BlankSprite	;Remove Sprite from screen
			pop ix
			ld (IY+O_CollProg),254 	;Dead Enemy
			
			ld a,%11011111
			ld (PlayingSFX),a
EnemyNoCollide:
			ld bc,ObjectByteSize	;Move To next Enemy
			add iy,bc
		pop bc
		dec c
		jr nz,EnemyCollideLoop
EnemyNoTest:
		ld bc,ObjectByteSize		;Move to the next bullet
		add ix,bc
	pop bc
	djnz NextBulletLoop

	ld b,Enemies
	ld ix,ObjectArray
NextObjectLoop:
	push bc
		ld a,(ix+O_Xacc)
		or (ix+O_Yacc)
		call nz,BlankSprite		;only blank sprite if Accel!=0
		
		call DrawObject			;Draw enemy
		call ObjectCollision	;See if player collided

		ld a,(ix+O_CollProg)
		cp 254					;254=killed enemy
		jr nz,ObjectNotDead		
		
		call DoRandom
		and %00111111
		jr nz,ObjectNotDead		
		ld (ix+O_CollProg),0	;Respawn enemy
		
		call RandomizeEdgeObjectPosition
		ld a,%01000011			;Respawn sound
		ld (PlayingSFX),a
ObjectNotDead:
		ld bc,ObjectByteSize
		add ix,bc
	pop bc
	djnz NextObjectLoop


;Do Enemy Bullets	
	ld b,BulletCount
	ld ix,EnemyBulletArray
NextEnemyBulletLoop:
	push bc
		ld a,(IX+O_CollProg)
		cp 250
		jr nc,BulletPlayerNoCollide	;Bullet left screen?
	
		call BlankSprite			
		call DrawObject			
			ld a,(IX+O_Xpos)	;Collision detection
			ifdef CollisionMaskX
				and CollisionMaskX	;Strip a few bits (for tile systems)
			endif
			ld d,a
			ld a,(IX+O_Ypos)
			ifdef CollisionMaskY
				and CollisionMaskY	;Strip a few bits (for tile systems)
			endif
			ld e,a
		
			ld a,(PlayerX)		;Player Pos
			ifdef CollisionMaskX
				and CollisionMaskX	;Strip a few bits (for tile systems)
			endif
			ld b,a
			ld a,(PlayerY)
			ifdef CollisionMaskY
				and CollisionMaskY	;Strip a few bits (for tile systems)
			endif
			ld c,a
			call RangeTest		;Return C if collided limit
			jr nc,BulletPlayerNoCollide
			call PlayerHurt  	;Kill player
BulletPlayerNoCollide:
		ld bc,ObjectByteSize
		add ix,bc
	pop bc
	djnz NextEnemyBulletLoop

	ld a,(PlayingSFX)	;Play the current soundeffect (0=nosound)
	ld hl,PlayingSFX2
	cp (hl)
	jr z,NoSound		;See if sound has changed?
	ld (hl),a
	call ChibiSound		;if it has - update sound.
NoSound:
	xor a
	ld (PlayingSFX),a	;Mute sound
	ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
ApplyScore:
		ld de,Score			;Destination
		ld b,4				;Bytes of BCD
		call BCD_Add		;Add the score
	ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;						Lesson YQuest4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
DrawObject:	;IX=Object
	ld a,(IX+O_CollProg)		;255 = object unused
	cp 250
	ret nc

	ld a,(IX+O_Program)			;Check Animation routine for object
	or a
	jp z,ProgramOK	;Static
	cp 1
	jp z,Program1	;Regular
	cp 2
	jp z,Program2	;Faster Change
	cp 3
	jp z,Program3	;Shooting
	cp 4
	jr z,Program4	;Indecisive
	cp 5
	jr z,Program5	;Waiter
	cp 6
	jr z,Program6	;Seeker
	
Program6:					;6=Seeker
	call DoRandom
	and %11000000
	jr z,Program6Nomove		;Move 3 times in 4

	ld a,(PlayerX)			;Get Player pos
	ld h,a
	ld a,(PlayerY)
	ld l,a

	ld a,(IX+O_Xpos)		;Get Object Xpos
	ld b,a
	cp h
	jr z,Program6_Xok
	jr c,Program6_Xlow
	dec b					;Move Left
	jr Program6_Xok
Program6_Xlow:
	inc b					;Move Right
Program6_Xok:
	ld a,b
	ld (IX+O_Xpos),a		;Save Xpos

	ld a,(IX+O_Ypos)		;Get Ypos
	ld c,a
	cp l
	jr z,Program6_Yok
	jr c,Program6_Ylow
	dec c					;Move Up
	jr Program6_Yok
Program6_Ylow:
	inc c					;Move Down
Program6_Yok:
	ld a,c
	ld (IX+O_Ypos),a		;Save Ypos
	jp ProgramNoMoveB
Program6Nomove:
	call DoRandom			;Randomize fire direction
	ld (IX+O_Yacc),a
	call DoRandom
	ld (IX+O_Xacc),a
	and %00000001
	call z,EnemyFirebullet	;Randomly fire a bullet
	jp ProgramNoMove

Program5:			;5=Waiter
	call DoRandom
	ld b,a
	and %10000000
	jr z,Program5B		;Maybe Fire, Maybe Wait
	ld a,b
	and %00011100		;Chance of continued movement
	jr nz,ProgramOK
	ld a,b
	and %00000011		;Chance of direction change
	jr nz,ProgramNewDir
Program5B:			
	call DoRandom
	ld b,a
	and %00000011		;Chance of firing
	call z,EnemyFirebullet
	jp ProgramNoMove

Program4:			;4=Indecisive
	call DoRandom
	ld b,a
	and %00011100		;Chance of direction change
	jr z,ProgramOK
	jr ProgramNewDir
	
Program3:			;3=Shooting
	call DoRandom
	ld b,a
	and %00001111
	call z,EnemyFirebullet
	call DoRandom
	ld b,a
	and %11111100		;Chance of direction change
	jr nz,ProgramOK
	jr ProgramNewDir
	
Program2:			;2=Rarely Change Direction
	call DoRandom
	ld b,a
	and %11111100		;Chance of direction change
	jr nz,ProgramOK
	jr ProgramNewDir
	
Program1:
	call DoRandom
	ld b,a
	and %00110000		;Chance of direction change
	jr nz,ProgramOK
	
ProgramNewDir:
	ld a,b
	and %00000001
	jr z,DontFlipY
	ld a,(IX+O_Yacc)
	neg					;Flip Y speed
	ld (IX+O_Yacc),a
ProgramProcessed:
DontFlipY:
	ld a,b
	and %00000010
	jr z,DontFlipX
	ld a,(IX+O_Xacc)
	neg					;Flip X Speed
	ld (IX+O_Xacc),a
DontFlipX:
	jr ProgramOk
	
ProgramNoMove:			;Static object
	ld a,(IX+O_Xpos)
	ld b,a
	ld a,(IX+O_Ypos)
	ld c,a
	jr ProgramNoMoveB
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ProgramOK:		;Finished handling movement code
	ld a,(IX+O_Xpos)
	ld (LastPosX),a		
	add (IX+O_Xacc)
	ld (IX+O_Xpos),a		;Update X
	ld b,a

	ld a,(IX+O_Ypos)
	ld (LastPosY),a
	add (IX+O_Yacc)
	ld (IX+O_Ypos),a		;Update Y
	ld c,a
	
ProgramNoMoveB:
;X Boundary Check - if we go <0 we will end up back at &FF
	ld a,b
	cp ScreenObjWidth 		
	jr c,ObjectPosXOk		;Not Out of bounds X
	ld a,(IX+O_Xacc)
	neg
	ld (IX+O_Xacc),a
	jr ObjectReset 			;Player out of bounds - Reset!
	
ObjectPosXOk:
;Y Boundary Check - only need to check 1 byte
	ld a,c
	cp ScreenObjHeight
	jr c,ObjectPosYOk		;Not Out of bounds Y
	ld a,(IX+O_Yacc)
	neg
	ld (IX+O_Yacc),a
						;Player out of bounds - Reset!
						
ObjectReset:
	ld a,(LastPosX) 		;Reset Xpos	
	ld b,a
	ld (IX+O_Xpos),a	
	ld a,(LastPosY)			;Reset Ypos
	ld c,a
	ld (IX+O_Ypos),a

	ld a,(IX+O_CollProg)
	cp 3					;Is object Bullet?
	jr nz,ObjectNotBullet
;Object is a bullet	
	
	call BlankSprite		;Bullet offscreen - clear sprite
	ld a,255
	ld (IX+O_CollProg),a	;Kill the bullet
	ret
	
ObjectNotBullet:
ObjectPosYOk:
	ld a,(IX+O_HSprNum)
	dec a					;0 = software tile
	cp 128					;
	jp nc,DoGetSpriteObj	;255 = Software Tile
	cp 255 
	jp nz,DoGetHSpriteObj	;0-127 = Hardware sprite
	jp DoGetSpriteObj
	
	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;						Lesson YQuest5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	

ObjectCollision:	;See if object has hit the player (object in IX)
	ld a,(IX+O_CollProg)
;1=Crystal 0=anything 3=Bullet (player) 255=nothing 254=dead	
	cp 250						;Collision routine >250 = disabled object
	ret nc		
	push bc
	push de
		ld a,(PlayerX)
		ifdef CollisionMaskX
			and CollisionMaskX	;Strip a few bits (for tile systems)
		endif
		ld b,a
		
		ld a,(PlayerY)
		ifdef CollisionMaskY
			and CollisionMaskY	;Strip a few bits (for tile systems)
		endif
		ld c,a
		ld a,(IX+O_Xpos)
		ifdef CollisionMaskX
			and CollisionMaskX	;Strip a few bits (for tile systems)
		endif
		ld d,a
		ld a,(IX+O_Ypos)
		ifdef CollisionMaskY
			and CollisionMaskY	;Strip a few bits (for tile systems)
		endif
		ld e,a
		call RangeTest			;See if player has hit object?
	pop de
	pop bc
	ret nc						;NC=No collision
	ld a,(IX+O_CollProg)
	or a
	jr z,PlayerHurt				;0=Player has been hurt by enemy 
	
PlayerCrystal:					;Prog 1=Got Crystal
	call BlankSprite			;Remove old player sprite
	ld hl,BCD1
	call ApplyScore				;Add points to score
	ld a,%00001111
	ld (PlayingSFX),a			;Make a sound
	
	ld a,(Crystals)
	dec a
	ld (Crystals),a				;Decrease remaining crystals
	jp z,NextLevel				;Level complete

	cp OnscreenCrystals			;Only 5 max crystals shown onscreen
	jr c,ClearCrystal

;If we've still got more crystals to collect, respawn crystal	
	ld c,enemies	
	call SafeRandomizeObjectPosition ;Give Crystal a new position
	ret
ClearCrystal:
	ld a,255					;255= Unused object
	ld (IX+O_CollProg),a
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
;Prog 0=Hurts Player
PlayerHurt:
	ld a,(Invincibility)
	or a						;Is player invincible?
	ret nz
	
	xor a						;Player hurt - stop movement
	ld (PlayerAccX),a
	ld (PlayerAccY),a

	push ix
		ld IX,PlayerObject
		ld a,5					;DeathAnim sprite 5
		ld (PlayerObject),a 
		xor a					;Frame num
PlayerDeathAnim:
		push af
			ld (SpriteFrame),a	;Set frame of explosion
			call DrawObject		;Show player
		pop af
		push af
			rlca
			rlca
			rlca
			xor %11000111		;Loud noise
			call ChibiSound
			
			ld bc,&F000			;Delay
PlayerDeathAnimLoop:
			dec bc
			ld a,b
			or c
			jr Nz,PlayerDeathAnimLoop	;Wait
		pop af
		inc a					;Next Anim Frame
		cp 4
		jr nz,PlayerDeathAnim
		call BlankSprite		;Remove old player sprite
	pop ix
	
	call ResetPlayer			;Reset player to centre
	xor a
	call ChibiSound				;silence sound

	ld a,(lives)				;Any lives left?
	or a
	jr z,PlayerDead				;No then game over.
	dec a
	ld (lives),a				;decrease life count
	ret
		
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
PlayerDead:
	ifdef ScreenWidth32
		ld hl,&020B			;Text position
	endif
	
	ifdef ScreenWidth40
		ld hl,&060C
	endif
	ifdef ScreenWidth20
		ld hl,&0609
	endif
	call Locate
	ld hl,txtDead
	call PrintString		;Show Player Dead Message

	ld hl,Score
	ld de,HiScore
	ld b,4
	call BCD_Cp				;Check if we have a new highscore
	jr nc,GameOverWaitForFire	;Jump if No highscore
	
;New Highscore
	ifdef ScreenWidth32
		ld hl,&0A0D			;New Highscore message pos
	endif
	ifdef ScreenWidth40
		ld hl,&0E10
	endif
	ifdef ScreenWidth20
		ld hl,&050B
	endif
	call Locate
	ld hl,txtNewHiscore			
	call PrintString		;Show the 'new highscore' message
;Transfer score to highscore
	ld bc,4
	ld hl,Score
	ld de,HiScore
	ldir					;update the highscore
	
GameOverWaitForFire:
	call WaitForFire
	jp ShowTitle
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

NextLevel:
	ifdef ScreenWidth32
		ld hl,&090B			;Level complete message pos
	endif
	ifdef ScreenWidth40
		ld hl,&0D0C
	endif
	ifdef ScreenWidth20
		ld hl,&0309
	endif
	call Locate
	ld hl,txtComplete		;ShowLevelComplete
	call PrintString

	ld a,(lives)			;Extra life every game
	inc a	
	ld (lives),a

	ld a,(Level)
	inc a
	ld (Level),a
	jp StartLevel			:;Init new level
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PlayerFirebullet:
	push de
	push hl
	push bc
		ld iy,BulletArray	;Check if there are any spare bullets
		ld b,BulletCount
CheckNextBullet:
		ld a,(iy+O_CollProg)
		cp 250
		jr nc,FoundBullet	;Yes! - create a bullet
		ld de,ObjectByteSize
		add iy,de
		djnz CheckNextBullet
CheckBulletReturn:
	pop bc
	pop hl
	pop de
	ret
	
FoundBullet:				;Player can fire
	ld a,%00000001 
	ld (PlayingSFX),a		;Make bullet sound
	ld bc,0
	ld a,(PlayerAccX)		;Fire bullet depending on player direction
	or a
	jr z,XZero
	ld b,-4					;Left
	cp 127
	jr nc,Xnegative
	ld b,4					;Right
Xnegative:
XZero:
	ld a,(PlayerAccY)
	or a 
	jr z,YZero
	ld c,-8					;Up
	cp 127
	jr nc,Ynegative
	ld c,8					;Down
Ynegative:
YZero:	
	ld a,b					;X and Y=0? no bullet
	or c
	jr z,CheckBulletReturn	

	ld a,(PlayerX)			;Bullet starts at player position
	ld (iy+O_Xpos),a
	ld a,(PlayerY)
	ld (iy+O_Ypos),a
	
	ld (IY+O_SprNum),6		;Bullet Sprite
	ld (IY+O_Xacc),b		;Bullet Acceleration
	ld (IY+O_Yacc),c

	ld (IY+O_Program),0		;Bullet program
	ld (IY+O_CollProg),3 	;Bullet collision routine  
	jr CheckBulletReturn

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EnemyFirebullet:
	push de
	push hl
	push bc
		ld iy,EnemyBulletArray		;Check if there are any spare bullets
		ld b,BulletCount
EnemyCheckNextBullet:
		ld a,(iy+O_CollProg)		;Yes! - create a bullet
		cp 250
		jr nc,EnemyFoundBullet
		ld de,ObjectByteSize
		add iy,de
		djnz EnemyCheckNextBullet
EnemyCheckBulletReturn:
	pop bc
	pop hl
	pop de
	ret
	
EnemyFoundBullet:
	ld a,%11000011			;Make Bullet sound
	ld (PlayingSFX),a

	ld bc,0
	ld a,(IX+O_Xacc)		;Convert Enemy Accel to Bullet Accel (X)
	or a
	jr z,EnemyXZero
	ld b,-2					;Left
	cp 127
	jr nc,EnemyXnegative
	ld b,2					;Right
EnemyXnegative:
EnemyXZero:
	ld a,(IX+O_Yacc) 		;Convert Enemy Accel to Bullet Accel (Y)
	or a 
	jr z,EnemyYZero
	ld c,-2					;Up
	cp 127
	jr nc,EnemyYnegative
	ld c,2					;Down
EnemyYnegative:
EnemyYZero:	
	ld a,b					;X and Y=0? no bullet
	or c
	jr z,EnemyCheckBulletReturn	

	ld a,(IX+O_Xpos)		;IX=Enemy
	ld (iy+O_Xpos),a		;IY=Bullet
	ld a,(IX+O_Ypos)
	ld (iy+O_Ypos),a
	
	ld (IY+O_SprNum),7		;Bullet Sprite
	ld (IY+O_Xacc),b		;Movement speed
	ld (IY+O_Yacc),c

	ld (IY+O_Program),0
	ld (IY+O_CollProg),3
	jr EnemyCheckBulletReturn
	
	
SetHardwareSprites:	; IX=Object B=Count C=SpriteNum
	ld (IX+O_HSprNum),c	;HSpriteNum
	ld de,ObjectByteSize
	add ix,de
	inc c
	djnz SetHardwareSprites
	ret
	
	