

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

; 		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

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

;Pick a random horizontal location
RandomXpos:				
	ld b,VscreenMinX+16				;Min
	ld c,VscreenMinX+VscreenWid-24	;Max
	call DoRangedRandom	
	ld (IY+O_Xpos),a
	ret
	
;Pick a random Vertical Position	
RandomYpos:				
	ld b,VscreenMinY+8			;Min
	ld c,VscreenMinY+VscreenHei-16;Max
	call DoRangedRandom
	ld (IY+O_Ypos),a
	ret

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

; 		Score Display

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
ShowScore:						;Show the score text
	ld a,-32
	ifdef ScreenSize256
		ld hl,&1D16
	else
		ld hl,&2017
	endif
	call LocateXYZ
	
	ld hl,txtScore
	call PrintString			;Score Text String
	
RefreshScore:
	ld a,-32
	ifdef ScreenSize256
		ld hl,&1C17
	else
		ld hl,&1F18
	endif
	call LocateXYZ

   	ld de,Score		
   	ld b,4
   	call BCD_Show				;Score BCD Number
	ret

ApplyScore:	;HL=Score to add	BCD1/BCD5/BCDX
	push iy
		push hl
			call RefreshScore
		pop hl
		ld de,Score			;Destination
		ld b,4				;Bytes of BCD
		call BCD_Add		;Add the score
		call RefreshScore
	pop iy
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Cldir:			;Fill BC+1 Bytes from DE with Zero
	xor a
CldirAlt:
	ld h,d
	ld l,e
	ld (hl),a
	inc de
	ldir
	ret
	
	


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

; 		Range Test

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

RangeTestIY:		;DE=Shot position IY=Object

	ld h,(IY+O_Wid)		;Object Width
	ld l,(IY+O_Hei)		;Object Height
	
	call GetGun
	ld a,(IX+G_Mode)	;Shotgun has wide speard
	cp 1
	jr z,ShotGunSpread	;Double range
	sra h			
	sra l
ShotGunSpread:			
RangeTestSpecIY:		;See if object XY pos DE hits IY 
										;(with radius HL)
	ld b,(IY+O_Xpos)	;Object position
	ld c,(IY+O_Ypos)

RangeTest2:		;See if object XY pos DE hits object BC 
	push bc								;(with radius HL)
		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
	ret
	
RangeTestOutOfRange:
		or a		;OK = Clear Carry
	pop bc
	ret

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

; 		Gun

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



ShowGunName:
	ld a,-32				;Zpos
	ifdef ScreenSize256
		ld hl,&0516			;Screen pos of 1st line
	else
		ld hl,&0117
	endif
	call LocateXYZ

	call GetGun				;Get address of the gun
	ld e,(ix+G_NameL)
	ld d,(ix+G_NameH)
	ex de,hl
	call PrintString		;Show gun name
	
	push hl
		ifdef ScreenSize256
			ld hl,&0517		;Screenpos of 2nd line
		else
			ld hl,&0118
		endif
		call Locate
	pop hl
	inc hl
	call PrintString		;Show second line of the gun name
	ret

GetAmmo:	
	push de
		ld ix,GunAmmo		;Get currently loaded ammo (Ram)
		ld d,0
		ld a,(GunNum)
		rlca				;2 bytes per Ammo
		ld e,a
		add ix,de
	pop de
	ret

GetGun:						;Get settings of selected gun (Rom)
	push de
		ld ix,GunConfig
		ld d,0
		ld a,(GunNum)
		rlca				;8 bytes per gun
		rlca
		rlca
		ld e,a
		add ix,de
	pop de
	ret


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

; 		Reload

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

ReloadMagazine:
	call ShowBullets
ReloadMagazineB:
	call GetAmmo

	ld a,(IX+A_Bullets)			;Get current loaded bullet count
	ld e,a

	push ix
		call GetGun				
		ld a,(IX+G_Magazine)	;Get Bullets per mag
	pop ix
	sub e
	jr z,SwapGun				;Gun already full?
	ld d,a						;Bullets left to fill the gun.

	ld a,(IX+A_Magazine)		
	or a
	jr z,SwapGun				;No bullets left to reload?

	cp d						;How many bullets can we load?
	jr nc,BulletsLeft			
	ld d,a						;Add Some bullets, but not full mag.
BulletsLeft:
	sub d	
	ld (IX+A_Magazine),a		;Take bullets from total

	ld a,d
	add e
	ld (IX+A_Bullets),a			;Add bullets to gun

	ld a,255
	ld (GunAmmo+1),a			;First Gun has inf ammo

	ld hl,SndReload				;Sound Seq
	ld a,20						;Priority
	call PlaySoundSeq			;Make sound 
	jp ShowBullets

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

; 		Swap Gun

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

SwapGun:
	call ShowGunName		;Remove Gun Name

	ld iy,SprGun
	ifdef SetRemoveSprite
		call SetRemoveSprite ;For SMS (No Xor)
	endif 
	call DrawSpriteFromIY	;Remove gun icon
	
NextGun:
	ld a,(GunNum)
	inc a
	and %00000011			;Switch to next gun (0-3)
	ld (GunNum),a

	call GetAmmo
	ld a,(IX+A_Bullets)
	or (ix+A_Magazine)
	jr z,NextGun			;Skip weapon if no ammo
SwapGunSpecific:
	ld a,(GunNum)
	push af
		call RemoveOldCursor	;Each gun has a different cursor
	pop af
	push af
		ifdef BuildMSX2			;We need to calculate the sprite nmber
			rlca
			rlca
			rlca
			ld h,0				;8 pixels per sprite
			ld l,a
		else
			rlca
			rlca
			rlca
			rlca				;16 bytes per sprite
			ld b,0
			ld c,a
			ld hl,SprCursorSprite
			add hl,bc	
		endif
		ld (CrosshairSprite),hl ;Pointer to sprite bitmap data
		call ShowCursor
	pop af
	
	add GunSprite			;Gun Sprite Offset
	ld (iy+O_SprNum),a
	call DefineSpriteIY		;Update Gun icon
	
	ifdef SetDrawSprite
		call SetDrawSprite	;For SMS (No Xor)
	endif 
	
	call DrawSpriteFromIY	;Redraw Gun sprite
	call ShowGunName		;Show Gun text
	call ShowBullets		;Show Bullet count

	ld hl,SndGunSwap		;Sound Sequence
	ld a,40					;Priority
	call PlaySoundSeq		;Play sound
	ret




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

; 		Fire

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


FireBullet:
	ld a,(NewPosY+1)	
	cp 22*8
	jp nc,ReloadMagazine	;Shot hud?

FireBullets:				;Minigun Version

	call GetAmmo			;Check loaded bullets
	ld a,(IX+A_Bullets)
	or a
	ret z					;No bullets left!

	ld a,(NewPosY+1)
	cp 22*8
	ret nc					;Shot HUD !
	
	srl a
	add 2
	add VscreenMinY			;Calc shot pos Y
	ld e,a

	ld a,(NewPosX+1)
	ifdef BuildZXN
		srl a				;Alter Cursor pos to logical units
	endif
	ifdef BuildCPC
		sla a
	endif 
	add VscreenMinX			;Calc shot pos X
	ld d,a

	push de
		call ShowBullets	;Remove old bullet text
		dec (IX+A_Bullets)	;Decrease bullets
		call ShowBullets	;Show new bullet text
	pop de

	ld b,SpriteDataCount 	;Scan enemies to see what was shot
	ld iy,SpriteArray
TestNextSprite:
	push bc
	push de
		push iy	
			bit 7,(IY+O_Program)	;Is sprite dead? Bit 7
			jr nz,TestSpriteDead
			bit 6,(IY+O_Program)	;Is sprite shootable? Bit 6
			
			call nz,TestSpriteIY	;See if sprite was shot
TestSpriteDead:
		pop iy
		ld bc,SpriteDataSize		;Move to next sprite
		add iy,bc
	pop de
	pop bc
	djnz TestNextSprite				;Repeat

	ld hl,SndFire					;Shoot sound Pew Pew!
	ld a,1							;Priority
	call PlaySoundSeq				;Play Sound
	ret


TestSpriteIY:	
	call RangeTestIY				;Range check sprite IY 
	ret nc							;Return if not hit

	ld hl,SndFireHit				;Sound sequence
	ld a,10							;Priority
	call PlaySoundSeq				;Play sound sequence

	ld a,(IY+O_Program)
	and %00111111					;Check enemy type and execute 
	cp prgBat2							;appropriate code
	jr z,HitEnemy
	cp prgBat1
	jr z,HitEnemy
	cp prgCorona
	jr z,HitEnemy
	cp prgChibiko
	jr z,HitEnemy
	cp prgPowerup
	jr z,HitGun
	ret								;Unknown object

HitEnemy:							;Hit an enemy
	call GetGun
	ld a,(IY+O_Life)				;Life of enemy 
	sub (IX+G_Power)				;Remove gun damage
	ld (IY+O_Life),a
	jr z,SpriteDead					;See if sprite is dead?
	jr c,SpriteDead
	ret

SpriteDead:							;We killed it - yay!
	ld hl,BCD1
	call ApplyScore					;HL=Score to add
	
	ld hl,SndFireHitDead			;Sound sequence
	ld a,12							;prority
	call PlaySoundSeq
	
	ifdef SetRemoveSprite
		call SetRemoveSpriteForceRedraw	;For SMS (No XOR)
	endif
	
	call DrawSpriteFromIY			;Remove the object
	
	ifdef SetDrawSprite						
		call SetDrawSprite			;For SMS (No XOR)
	endif
	
	jp SpriteOffscreen				;Kill and prep respawn


	
HitGun:								;Hit a gun or power up
	ifdef SetRemoveSprite
		call SetRemoveSprite		;For SMS (No XOR)
	endif
	
	call DrawSpriteFromIY			;Remove the object
	
	ifdef SetDrawSprite
		call SetDrawSprite			;For SMS (No XOR)
	endif

	ld a,60
	ld (iy+O_Life),a				;Timeout before next spawn

	ld a,(IY+O_SprNum)
	and %00111111
	sub GunSprite					;Test object type (0-3=gun 4=health)
	cp 4 
	jr z,Health
	push af
		call ShowBullets			;Remove bullet text
		call ShowGunName			;Remove gun name

		ld iy,SprGun
		call DrawSpriteFromIY		;Remove gun sprite
	pop af
	ld (GunNum),a					;Switch to the selected gun
	call SwapGunSpecific

	call ShowBullets				;Show the bullet text
	call GetGun
	ld a,(ix+G_Reload)				;Ammo collected
	push af
		call GetAmmo				;Get current gun ammo
	pop af
	add (ix+A_Magazine)				;Add collected ammo
	jr c,MagFull
	ld (ix+A_Magazine),a			
MagFull:
	ld iy,SprPowerup				;Set sprite offscreen
	ld (iy+O_Xpos),0
	
	call DrawSpriteFromIY			;Update SPrite
	
	jp ReloadMagazineB				;Load bullets into gun
	


Health:								;Collected health
	call ShowLife					;Remove health icons
	ld a,(PlayerLife)
	add 8							;Add 8 health units
	cp 32
	jr c,HealthOK
	ld a,32
HealthOK:
	ld (PlayerLife),a
	call ShowLife					;Show health icons

	ld hl,SndEnergy					;Sound Sequence
	ld a,20							;Priority
	call PlaySoundSeq				;Play sound
	
	ld iy,SprPowerup				;Set sprite offscreen
	ld (iy+O_Xpos),0
	ret
	
	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 		Life Bar

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

DecreaseLife:
	ld a,(PlayerLife)
	push af
		add 3
		ld h,a			;Calc Xpos from remaining life
	
		ld a,-10		;Zpos
		
		ifdef ScreenSize256
			ld l,&15	;Ypos
		else
			ld l,&16
		endif
		push hl
			call LocateXYZ
			ld a,128	;Remove Life Full
			call printchar
		pop hl
			ifdef BuildSMS
				call SetDrawSprite	;Sprite Routines to draw
			endif 
			call Locate
			ld a,127		;Redraw Life Empty
		call printchar
	pop af
	
	dec a					;Take a unit of life
	jp z,GameOver			;Player dead?
	ld (PlayerLife),a	

	ld hl,SndSucked			;Sound sequence
	ld a,10					;Priority
	call PlaySoundSeq		;Play sound
	ret
	
	
ShowLife:
	ld a,-10
	ifdef ScreenSize256
		ld hl,&0415		;4=Far left on 256 pixel screen
	else
		ld hl,&0016
	endif
	call LocateXYZ

	ifndef ScreenSize256
		ld a,'['		;Print 4 brackets on 320 pixel screens
		ld b,4
		call ShowLifeAgain
	endif
	
	call ShowLifeBar			;Show Full Blocks

	ld b,c						;Count
	ld a,c 
	or a						;No empty blocks?
	call nz,ShowEmptyLife		;Show Empty blocks

	ifdef ScreenSize256
		ret
	else
		ld a,']'		;Print 4 brackets on 320 pixel screens
		ld b,4
		jp ShowLifeAgain
	endif

ShowEmptyLife:
	ld a,127 					;Life Empty
	jr ShowLifeAgain
ShowLifeBar:
	ld c,32						;Total blocks
	ld a,(PlayerLife)			
	or a 
	ret z						;No life left
	ld b,a
	ld a,128					;Life Full

ShowLifeAgain:
	call printchar				;Show a life block
	dec c						;Drew one block
	djnz ShowLifeAgain			;Repeat for other blocks
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 		Bullet count

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

	
ShowBullets:
	ld a,-32					;Zpos
	ifdef ScreenSize256
		ld hl,&1416				;XyPos
	else
		ld hl,&1417
	endif
	push hl
		call LocateXYZ
		ld hl,TxtBullets		;Show "Bullet text"
		call PrintString
	pop hl
	inc l						;Next line
	call Locate
	call GetAmmo
	ld a,(IX+A_Bullets)
	push ix
		call ShowDecimal		;Bullets in gun

		ld a,'/'
		call PrintChar
	pop ix
	ld a,(IX+A_Magazine)
	call ShowDecimal			;Bullets in magazine
	ret


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

; 		Cursor

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

UpdateCursor:
	ld a,(OldPosX+1)			;Last Draw pos
	ld b,a
	ld a,(OldPosY+1)
	ld c,a

	ld a,(NewPosX+1)			;New Draw Pos
	ld h,a
	ld a,(NewPosY+1)
	ld l,a

	or a
	sbc hl,bc					;Is position the same?
	ret z						;Cursor doesn't need redraw
	
	call RemoveOldCursor		;Remove Old Sprite
	call ShowCursor				;Show New Sprite
	ret

RemoveOldCursor:
	ld a,(OldPosX+1)
	ld b,a
	ld a,(OldPosY+1)
	ld c,a
	jp ShowCrosshair

	ifndef ShowCursor			;Custom Routine?
ShowCursor:
		dodi					;Disable interrupts if needed
		
		ld hl,(NewPosX)
		ld de,(NewPosY)		

		ld (OldPosX),hl			;Update cursor pos
		ld (OldPosY),de

		ld a,(NewPosX+1)
		ld b,a
		ld a,(NewPosY+1)
		ld c,a
		call ShowCrosshair		;Show cursor
		
		DoEI					;Re-enable interrupts if needed		
		ret
	endif


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

;		Redraw the screen (after CLS)

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

RePaintScreen:
	call RepaintSprites			;Show objects
	call ShowCursor				;Show Cursor
	call ShowBullets			;Show Bullet count
	call ShowLife				;Show lifebar
	call ShowGunName			;Show Gun name
	call ShowScore				;Show score
	ret

RepaintSprites:
	ld iy,SprPowerup
	call DrawSpriteFromIY		;Show powerup object
	
	ld iy,SprGun
	call DrawSpriteFromIY		;Show gun in hud.
	
	ld iy,SprMoon
	call DrawSpriteFromIY		;Show the moon

;As they don't move We use one mountain sprite for the 
;whole screen.
	
	ld iy,SprMountain			;Shown the mountains
	ld b,12						;Mountains onscreen
DrawMountainAgain:
	ld a,b		
	sla a						;Width of one mountain
	sla a
	sla a
	sla a
	add &0C						;First mountain Xpos
	ld (IY+O_Xpos),a
	push bc
		call DrawSpriteFromIY	;Draw a mountain
	pop bc
	djnz DrawMountainAgain

	ld iy,sprClouds
	ld b,CloudCount
	call DrawNSpritesFromIY		;Draw all the clouds
	
	ld iy,sprGrass
	ld b,GrassCount 
	call DrawNSpritesFromIY		;Draw all the grasses

	ld iy,SprRock
	ld b,RockCount  
	call DrawNSpritesFromIY		;Draw all the rocks

	ld iy,SprEnemies
	ld b,EnemyCount  
	call DrawNSpritesFromIY		;Draw all the baddies (Booo!)
	ret


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

;		Sprite crop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
DoCrop:						;BC = XY Pos ;IX=HW Sprite size 
	push de
	push iy		
		ld iy,0				;Amount to L/R Crop

	;Crop Left hand side
		ld a,b
		sub VscreenMinX 	;64 = Leftmost visible tile
		jr nc,NoLCrop
		neg
		cp ixl				;Check width - No pixels onscreen?
		jr nc,DrawSpriteFromIY_Abandon	;Offscreen
		ld iyl,a			;Left crop
		xor a
NoLCrop:
	;Crop Right hand side
		ld b,a				;Zero xpos
		add ixl
		sub VscreenWid-VscreenWidClip	;Logical Width of screen
		jr c,NoRCrop
		cp ixl				;Check width - No pixels onscreen?
		jr nc,DrawSpriteFromIY_Abandon	;Offscreen
		ld iyh,a			;Right Crop
		
NoRCrop:
		ld a,iyh		
		add iyl
		srl a			;Total cropped>0?
		jr z,NoClip

		ld a,iyl			;Left Clip
		add iyh				;Right Clip
		srl a				;4 pixels sprite per byte (2 logical units)
		ld (SpriteHClipPlus2-2),a	;Set crop amount 
							;(bytes to miss per line)

		ld a,ixl			;Width
		sub iyl				;Left Clip
		sub iyh				;Right Clip
		ld ixl,a			;Update Width
	
		ld hl,GetNextLineWithClip		;Turn on Byteskip routine
		ld (NextLineCommand1_Plus2-2),hl
		ld (NextLineCommand2_Plus2-2),hl
		
		ld a,iyl			;Left hand crop
		srl a
		ld h,0
		ld l,a				;Bytes to miss from start of sprite
	pop iy
	pop de

	add hl,de		
	ex de,hl				;DE=Source Pos

	jr ClipDone
NoClip:
	pop iy				;Nothing was clipped
	pop de
ClipDone:

	ifndef pixelsPerByte2
		srl b			;Halve X (CPC - 4 pixels per logical)
	endif
	sla c				;Double Y (2 lines per logical)

	ld a,ixh			;Height
	sla a				;2 lines per logical unit
	ld ixh,a

	ld a,ixl			;Width
	inc a				;2 logical units per byte 
	srl a					;(4 pixels per byte 2bpp)
	ld ixl,a

	or a				;Clear Carry (Data to show)
	ret

DrawSpriteFromIY_Abandon:	;No pixels to draw
	pop iy
	pop de
	scf					;Set Carry (Nothing to show)
	ret
	

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

;		Sprite Settings

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

GetSpriteSettingsFromIY:
	ld a,(IY+O_Hei)
	ld ixh,a
	srl a				;Halve height
	neg
	add (IY+O_Ypos)
	ld c,a				;Center of sprite Ypos

	ld a,(IY+O_Wid)	
	ld ixl,a
	srl a				;Halve width
	neg
	add (IY+O_Xpos)	
	ld b,a				;Center of sprite pos
	
	ifndef SpriteMSXData
		ld e,(IY+O_SprL) ;on bitmap systems DE is source data	
		ld d,(IY+O_SprH)	
	else 
		ld de,0			;on MSX DE is X Crop
	endif
	ret

	
	;Update Sprite Graphic
UpdateSpriteFromIY:	
	bit 7,(IY+O_SprNum)	;Check if sprite is 3D (3x2 sprites)
	ret z

	ld a,(IY+O_Zpos)
	ld d,0				;Spriteset (0=Front)
	cp 128
	jr nc,SprZDone
	inc d				;1=Middle
	cp 48
	jr c,SprZDone
	inc d				;2=Back

SprZDone
	sla d				;Sprite *2 (0/2/4)
	bit 7,(iY+O_FrameTick)
	jr z,SprFrame2
	inc d				;Alt Frame (0/2/4 or 1/3/5)
SprFrame2:
	ifdef SpriteMSXData
		ld (IY+O_SprH),d ;For MSX2 type sprites Framenum
	endif
	ld a,(IY+O_SprNum)
	and %00111111		;Get Base Sprite Number
	add d				;Add offset (0-5)
	
	
	;Get Sprite settings from def list
DefineSpriteIY:
	ld ix,SpriteInfo	;Sprite Defs (Exported from AkuSprite)
	sla a
	sla a				;4 bytes per def
	ld e,a
	ld d,0
	add ix,de			;Calculate Sprite data 
	
	ifndef SpriteMSXData
		ld a,(ix+S_AddressL)
		ld (iy+O_SprL),a
		ld a,(ix+S_AddressH)
		ld (iy+O_SprH),a	;Get Sprite Bitmap address
	endif
	
	ld a,(ix+S_Wid)		;Get Width
	srl a				;Convert to logical address
	ld (iy+O_Wid),a

	ld a,(ix+S_Hei)		;Get Height
	srl a				;Convert to logical address
	ld (iy+O_Hei),a
	ret


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

;		Sprite Init

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			
	
SetSpritesRandomDepth:		;Used for 3d objects like clouds and grass
	push bc					;Rand D-E... B objects
	push de
		ld b,d				;Min
		ld c,e				;Max
		call DoRangedRandom	;Select number between B and C

		ld hl,RandPerspective
		sla a				;4 bytes per random pos
		sla a
		ld b,0
		ld c,a
		add hl,bc			;Select it
	
		ld a,(hl)
		inc hl
		ld (IY+O_Zpos),a	;Set Zpos

		ld a,(hl)
		inc hl
		ld (IY+O_AnimTick),a ;Set Speed

		ld a,(hl)
		ld (IY+O_Ypos),a	;Set Ypos

		ld bc,SpriteDataSize
		add iy,bc			;Move To Next

	pop de
	pop bc
	djnz SetSpritesRandomDepth
	ret

	
SetSpritesRandomN: ;Set a parameter to Rand D-E... B objects
	push bc				;(Eg Random Xpos)
	push de
		ld b,d				;Min
		ld c,e				;Max
		call DoRangedRandom
		ld (IY),a			;Set parameter
		
		ld bc,SpriteDataSize	
		add iy,bc			;Move to next

	pop de
	pop bc
	djnz SetSpritesRandomN
	ret

	
	;Set up sprites from a template
DefineSprite:					
	ld b,1
DefineSprites:				;Set B sprites from DE to settings from HL
	ld a,b	;B=SpriteCount
	or a
	ret z					;No sprites to copy!
	
	push iy
	push ix
DefineSpritesB:
		push bc	
		push hl
			push de			;IY/DE=Dest sprite
			pop iy

			ld bc,SpriteDataSize
			ldir				;Copy one sprite

			ifdef SpriteMSXData
				ld (IY+O_SprH),0	;For MSX2 type sprites Framenum
			endif
	
			push de
				ld a,(IY+O_SprNum)
				call DefineSpriteIY	;Define sprite settings from A
			pop de
		pop hl
		pop bc
		djnz DefineSpritesB
	pop ix
	pop iy
	ret

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

;		Sprite Draw

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
DrawSpriteFromIY:
	bit 7,(IY+O_Program)			;Dead?
	ret nz

	call GetSpriteSettingsFromIY	;Get Sprite W/H X/Y Ram
	call DoCrop						;Crop the sprite
	ret c							;Nothing left to draw?

	call GetScreenPos	;BC=XY DE=SpriteHL IX=SpriteWH

	call ShowSpriteL				;Show Left Eye

	call GetSpriteSettingsFromIY

	ld hl,GetNextLine				;Reset Sprite Engine
	ld (NextLineCommand1_Plus2-2),hl
	ld (NextLineCommand2_Plus2-2),hl
	
	ld a,(Depth3D)					;3D Depth scale
	push bc
		ld b,a
		ld a,(IY+O_Zpos)			;Get Z pos
		
		sra a		;Z Scale
		sra a		;Z Scale
SpriteZScale:
		sra a		;Z Scale
		djnz SpriteZScale
	pop bc

	add b					;New Xpos
	ld b,a

	call DoCrop
	ret c			;Nothing left to draw?
	
	call GetScreenPos	;BC=XY DE=SpriteHL IX=SpriteWH

	call  ShowSpriteR	;Show Right Eye

	ld hl,GetNextLine					;Reset Sprite Engine
	ld (NextLineCommand1_Plus2-2),hl
	ld (NextLineCommand2_Plus2-2),hl
	ret

	
	
DrawNSpritesFromIY:			;Draw B sprites from IY
	push bc
		push iy	
			call DrawSpriteFromIY	;Draw A sprite
		pop iy
		
		ld bc,SpriteDataSize
		add iy,bc			;Move to next
		
	pop bc
	djnz DrawNSpritesFromIY
	ret

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

;		Print Char

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

LocateXYZ:
	ld (CursorZ),a		;Load X, Y and Z
locate:
	ld (CursorY),hl		;Load X and Y
	ret

	
locateAndPrint:			;Print string HL at DE
	ld (CursorY),de
PrintString:
	ld a,(hl)			;Print a '255' terminated string 
	cp 255
	ret z
	inc hl
	call PrintChar		;Platform specific
	jr PrintString

NewLine:				;Move Down a Line
	xor a
	ld (CursorX),a
	ld a,(CursorY)
	inc a
	ld (CursorY),a
	ret


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

; ChibiSound - Sound Sequence 

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


UpdateSoundSequence:
	ld hl,SoundTimeout
	ld a,(hl)
	or a
	jr z,SoundSilent			;Sequence ended

	dec (hl)					;Reduce timeout
	ret nz						;Reached zero?

	ld hl,(SoundSequence)		
	ld a,(hl)					;Get new Timeout
	inc hl
	ld (SoundTimeout),a
	ld a,(hl)					;Get New Chibisound byte
	inc hl
	ld (SoundSequence),hl		;Update SoundSequence pos
	call ChibiSound				;Make sound
	ret
	
	
SoundSilent:
	ld (SoundPriority),a		;Zero priority
	ret

PlaySoundSeq:
	push hl
		ld hl,SoundPriority		
		cp (hl)					;Only play sequence if priority
		jr nc,PlaySoundHigher		;is higher than current one
	pop hl
	ret
PlaySoundHigher:
		ld (hl),a				;Set new priority
	pop hl
	ld (SoundSequence),hl
	ld a,1
	ld (SoundTimeout),a
	ret



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

; 		Sprite Animation

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

AnimateSpriteIY:		;Update Sprite object

	ld hl,(Tick)
	ld a,(gamespeed)	;Skip many game ticks
	and l
	ret nz
	
	ld a,h
	and (IY+O_AnimTick)	;Update based on object tick?
	ret nz
	
	bit 7,(IY+O_Program)
	jp nz,AnimateDeadSprite	;Is sprite even alive?

	push iy
		ifdef SetRemoveSprite
			Call SetRemoveSprite	;SMS can't XOR!
		endif	
		call DrawSpriteFromIY		;Remove old sprite
	pop iy

	ld a,(iy+O_FrameSpeed)
	add (iy+O_FrameTick)			;Update frame tick
	ld (iy+O_FrameTick),a

	call UpdateSpriteFromIY			;Update Sprite bitmap 


	ld a,(IY+O_Program)
	and %00111111					;Get object type
	
	cp prgChibiko
	jr nz,NotChibikoSprite

;;;;;;;;;;;;;;;;;;;;;;;;; Start of Chibiko Code!	
	
;Chibiko flys around the screen in a 3D 'zig zag' type motion	
	
	ld a,(IY+O_ypos)
	cp 16
	jr c,NegChibikoSpriteY			;Chibiko sprite at top
	cp 96-16
	jr nc,NegChibikoSpriteY			;Chibiko sprite at bottom
	jp TestChibikoX
	
NegChibikoSpriteY:
	ld a,(IY+O_MoveY)
	neg								;Flip movement direction Y
	ld (IY+O_MoveY),a
	
TestChibikoX:
	ld a,(IY+O_xpos)
	cp VscreenMinX+16				;Chibiko sprite at Left
	jr c,NegChibikoSpriteX
	
	cp VscreenMinX+VscreenWid-16	;Chibiko sprite at Right
	jr nc,NegChibikoSpriteX
	jp TestChibikoZ
	
NegChibikoSpriteX:
	ld a,(IY+O_MoveX)
	neg								;Flip movement direction X
	ld (IY+O_MoveX),a

TestChibikoZ:
	ld a,(IY+O_Zpos)				;Check Z Pos

	cp 88							;Chibiko at Back of screen
	jr c,NotChibikoSprite
	cp -88							;Chibiko at Front of screen
	jr nc,NotChibikoSprite
	jp NegChibikoSpriteZ
NegChibikoSpriteZ
	ld a,(IY+O_MoveZ)
	neg								;Flip movement direction Z
	ld (IY+O_MoveZ),a
NotChibikoSprite:

;;;;;;;;;;;;;;;;;;;;;;;;; End of Chibiko Code!

	ld a,(IY+O_Xpos)
	add (IY+O_MoveX)				;Update Xpos
	
	cp 255-8						;Off Right of screen?
	jp nc,SpriteOffscreen			;Yes! Kill sprite!
	cp 8							;Off Left of screen
	jp c,SpriteOffscreen			;Yes! Kill sprite!
	ld (IY+O_Xpos),a

	
	ld a,(IY+O_Program)
	and %00111111					;Get object type
	cp prgMoon
	jr nz,NotMoon					;Is sprite moon?

;;;;;;;;;;;;;;;;;;;;;;;;; Start of Moon Code!
	
	ld a,(IY+O_Xpos)				;Check Xpos
	add (IY+O_MoveX)

	cp &30-1						;At left of screen?
	jp c,SpriteOffscreen_Moon

	cp VscreenWid+VscreenMinX
	jr c,NotMoon					;At right of screen?
	
SpriteOffscreen_Moon:		;Level has ended
	pop af
	pop af
	pop af
	
	ld hl,BCDX				;New level score
	call ApplyScore			;HL=Score to add	BCD1/BCD5/

	ld hl,LevelNum
	inc (hl)				;Inc levelnum
	
	jp StartNewLevel		;start new level
	
	
NotMoon:
;;;;;;;;;;;;;;;;;;;;;;;;; End of Moon Code!


	ld a,(IY+O_Ypos)
	add (IY+O_MoveY)		;Update Ypos
	ld (IY+O_Ypos),a

	ld a,(IY+O_Zpos)
	add (IY+O_MoveZ)		;Update Zpos

	cp -96
	jr nc,ZposOK
	cp 96
	jr c,ZposOK
	cp 128
	jr nc,SpriteForeGround
	jr ZposNG
ZposOK:
	ld (IY+O_Zpos),a
	
	cp 128
	jr c,ZposNG			;Jump if not in the 'hurt zone' (foreground)
	cp -32
	jr nc,ZposNG
SpriteForeGround:
	ld c,%00000011

	ld a,(LevelNum)		;Hurt rate affected by level number
	cp 16
	jr c,SetHurtTickRate
	srl c				;Double hurt rate
	cp 32
	jr c,SetHurtTickRate
	srl c				;Quad hurt rate
SetHurtTickRate:
	ld a,(Tick)			;Should we hurt the player this tick
	and c				
	jr nz,ZposNG		;No hurt this tick

;;;;;;;;;;;;;; OUCH! - Hurt routine!
		
	ld a,(IY+O_Program)	
	and %00111111		;Check object program and run 
	cp prgCorona 			;'Hurt' rotuine
	jr z,SpriteHurtPlayerCorona
	cp prgChibiko
	jr z,SpriteHurtPlayer
	cp prgBat1 
	jr z,SpriteHurtPlayer
	cp prgBat2
	jr z,SpriteHurtPlayer
	jr ZposNG

SpriteHurtPlayerCorona:
	ld b,32					;Chibicorona hurts less per tick!
	jr SpriteHurtPlayerB
SpriteHurtPlayer:
	ld b,64					;Normal hurt routine
SpriteHurtPlayerB:

	ld hl,SndSuckedFraction	;You've been sucked!
	ld a,1
	call PlaySoundSeq

	ld a,(PlayerLifeFraction)
	add b					;Reduce partial life unit
	ld (PlayerLifeFraction),a
	jr nc,ZposNG
	
	push iy
		call DecreaseLife	;Reduce whole life unit
	pop iy
ZposNG:

;;;;;;;;;;;;;; NO pain this tick.

	ifdef SetDrawSprite
		Call SetDrawSprite		;Used By SMS (Can't do XOR)
	endif
	call DrawSpriteFromIY		;Draw new sprite
	ret


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

; 		Sprite went Offscreen 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	
SpriteOffscreen:
	set 7,(IY+O_Program)		;Kill the object (Dead)

	ld a,(IY+O_Program)
	and %00111111				;Check program
	cp prgBat1 
	jr z,SpriteOffscreen_Enemy
	cp prgBat2
	jr z,SpriteOffscreen_Enemy
	cp prgCorona 
	jr z,SpriteOffscreen_Corona
	cp prgChibiko 
	jr z,SpriteOffscreen_Chibiko
	cp prgPowerup 
	jr z,SpriteOffscreen_PowerUp
	cp prgBackground
	jr z,SpriteOffscreen_Background
	ret
	
	;'Life' is respawn rate 
	
SpriteOffscreen_PowerUp:
	ld b,20	
	ld c,40
	jr SpriteOffscreen_SetLife
	
SpriteOffscreen_Background:		;Grass/Cloud/Rock
	ld b,0
	ld c,2
	jr SpriteOffscreen_SetLife
	
SpriteOffscreen_Chibiko:		;Chibiko... Booo Hiss!
	ld b,250
	ld c,255
	jr SpriteOffscreen_SetLife
	
SpriteOffscreen_Corona:			;High respawn rate!
	ld b,3
	ld c,6
	jr SpriteOffscreen_SetLife
	
SpriteOffscreen_Enemy:			;General Bats
	ld b,5
	ld c,10
	
SpriteOffscreen_SetLife:		
	call DoRangedRandom
	ld (IY+O_Life),a			;Random Respawn time
	ret

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

; 		Sprite Dead

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


AnimateDeadSprite:
	dec (IY+O_Life)		;Stay dead when life>0
	ret nz

	ld a,(IY+O_Program)
	and %00111111		;What object are we respawning?
	
	cp prgBat1 
	jr z,AnimateDeadSprite_Enemy1
	cp prgBat2
	jr z,AnimateDeadSprite_Enemy2
	cp prgPowerup 
	jr z,AnimateDeadSprite_PowerUp
	cp prgBackground
	jr z,AnimateDeadSprite_Background
	cp prgCorona
	jr z,AnimateDeadSprite_Corona
	cp prgChibiko
	jr z,AnimateDeadSprite_Chibiko
	ret

AnimateDeadSprite_Chibiko:
	call InitEnemyObjectChibiko
	jr AnimateDeadSprite_ReanimatedWithSound
	
AnimateDeadSprite_Enemy1:
	call InitEnemyObject1
	jr AnimateDeadSprite_ReanimatedWithSound
	
AnimateDeadSprite_Enemy2:
	call InitEnemyObject2
	jr AnimateDeadSprite_ReanimatedWithSound

AnimateDeadSprite_Corona:
	call InitCoronaObject
	jr AnimateDeadSprite_ReanimatedWithSound

AnimateDeadSprite_PowerUp:	;Create a Gun/Healt

	ld b,0+64				;Min Y
	ld c,16+64				;Max X
	call DoRangedRandom
	ld (IY+O_Ypos),a

	ld a,(ScreenEdgeSpawnPos)	;Xpos
	ld (IY+O_Xpos),a		;Spawn at edge of screen

	ld a,-40		;Foreground
	ld (iy+O_Zpos),a

	ld a,1
	ld (iy+O_Life),a		;One hit 'kills' a powerup

	ld b,Spr3D+GunSprite+1	;Min
	ld c,Spr3D+GunSprite+5	;Max
	call DoRangedRandom		;Select a powerup (4 guns+life)
	ld (iy+O_SprNum),a
	call UpdateSpriteFromIY

	jr AnimateDeadSprite_Reanimated	

AnimateDeadSprite_Background:	;Cloud/Rock/Grass
	ld a,1
	ld (iy+O_Life),a			;Can't actually be shot.
	
	ld a,(ScreenEdgeSpawnPos)	;Set spawn to edge of screen
	ld (IY+O_Xpos),a
	jr AnimateDeadSprite_Reanimated

	
AnimateDeadSprite_ReanimatedWithSound:	
	ld hl,SndSpawn				;Respawn sound
	ld a,15
	call PlaySoundSeq

AnimateDeadSprite_Reanimated:
	res 7,(IY+O_Program)		;Resurrect enemy 
	call DrawSpriteFromIY
	ret


InitCoronaObject:			;Here's ChibiCorona!
	Call RandomXpos

	Call DoRandom		;Chibicoronas appear at top or bottom
	cp 128
	jr c,InitCoronaObjectTop

	ld b,26+Spr3D			;Top ChibiCorona
	ld a,10					;Top

	jr InitCoronaObjectB
InitCoronaObjectTop:
	ld b,24+Spr3D			;Bottom Chibicorona
	ifdef ScreenSize256
		ld a,64				;Bottom (small screen)
	else
		ld a,72				;Bottom (200 px tall screen)
	endif
InitCoronaObjectB:
	ld (iy+O_Ypos),a		;Set Ypos

	ld a,b
	ld (iy+O_SprNum),a		;Set Sprite number

	call CheckObjectPos		;Check if object overlaps another 
	jr c,InitCoronaObject		;(XOR will make it invisible!)

	ld a,1
	ld (iy+O_Life),a		;ChibiCorona is weak!

	call UpdateSpriteFromIY
	ret

CheckObjectPos:			;Check if object overlaps another (Bad for XOR)
	ld b,EnemyCount
	ld ix,SprEnemies			;IX=Current tested objec
CheckObjectPosB:
	push bc
		ld a,ixh
		cp iyh					;IY=New object to test.
		jr nz,CheckOk
		ld a,ixl
		cp iyl
		jr nz,CheckOk
		jr CheckSkip			;We're comparing object to itself... doh!
CheckOk:

		ld d,(ix+O_Xpos)		;Check for collide
		ld e,(ix+O_Ypos)
		ld hl,&0808				;8x8 units
		call RangeTestSpecIY;See if object XY pos DE hits IY (with radius HL)

		jr c,CheckCollide		;return carry=collide
CheckSkip:
	ld bc,16
	add ix,bc					;Move to next object
	pop bc
	djnz CheckObjectPosB
	or a						;Carry Clear
	ret 
CheckCollide:
	pop bc
	ret							;Carry Set
	
	
InitEnemyObjectChibiko:
	Call RandomXpos
	Call RandomYpos
	ld c,63						;Chibiko life

	call ScaleUpLifeC			;As level increases enemy gets tougher

	call UpdateSpriteFromIY		;Update sprite 
	ret


InitEnemyObject2:	
	ld c,2					;Gobby bat
	jr InitEnemyObjectB
InitEnemyObject1:	
	ld c,1					;Basic Bat
InitEnemyObjectB:

	call ScaleUpLifeC		;Bats get harder on later levels
PosNG:
	Call RandomXpos	;Randomize Location of object (both)
	Call RandomYpos

	call CheckObjectPos		;Check no overlap
	jr c,PosNG

	ld a,95					;Far Back in screen
	ld (iy+O_Zpos),a

	call UpdateSpriteFromIY	;update sprite
	ret


ScaleUpLifeC:
		ld a,(LevelNum)
		and %11000000
		jr z,NoIncresedLife	
		sla c				;Scale up life 1
NoIncresedLife:
		ld a,(LevelNum)
		and %00110000		;Scale up life 2
		jr z,NoIncresedLifeB
		sla c
NoIncresedLifeB:
	ld a,c					;Store new life
	ld (iy+O_Life),a
	ret	

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


GameOver:
	ld hl,SndGameover		;Gameover sound
	ld a,255				;Prority
	call PlaySoundSeq		;Play sound
	
	call DoPalFadeOutQ		;Fade out

	call Set2DMode
	call SetPaletteBlack	;Blackout screen

	
	ifdef BuildSMS
		ld a,(PaletteNum)
		push af
			ld de,GameOverR		;Tilemap
			ld bc,&0800			;XY Pos
			ld ixh,CachTilesR	;Cache loc
			
			call FillAreaWithTilesCustomQ
			ld de,GameOverL		;Tilemap
			ld bc,&0800			;XY Pos
			ld ixh,CachTilesL	;Cache loc
			call FillAreaWithTilesCustomQ
						
			ld IY,GameOverData	;Sprite Data to send
			ld de,&0
			ld H,8				;X*Y tilecount (8*32)
			ld l,32
			call DefineSpriteA	;DE=VDP address, IY=Source,H=Width,L=Height
	else
	
		ifdef SuckOfDeath2dTileMap		;Alternate tilemaps for 2D and 3D
			ld hl,SuckOfDeath2dTileMap	;2D Tilemap
			ld a,(Depth3D)
			or a
			jr z,GameOver2D
			ld a,1
			ld hl,SuckOfDeath3dTileMap	;3D Tilemap Ver
	GameOver2D:
			push af
			ld bc,SuckOfDeath3dTiles	;TileData
			ld iyh,12	;X
			ld a,0		;Y
			call	ShowTileMapAltPosQtr	;HL=Tilemap BC=Tile Data 
											;IYH=X	A=Y 	;E=Height ;D=Wid
			ld a,(Depth3D)
			or a
			call nz,DoSet3D		;Turn on stereoscopic render
		else
		
			ifndef Use3DTilemaps		;No Tilemap? (Test code)
				xor a
				push af
					ld a,(Depth3D)
					or a
					call nz,DoSet3D		;Turn on stereoscopic render
					
			else							;16 color 3d tilemap
				ld hl,SuckOfDeath3dTileMap	;3D tilemap used in 2d and 3d
				ld d,4						;2d mode
				ld a,(Depth3D)
				or a						
				jr z,GameOver2D
				ld d,1						;3d mode
				
		GameOver2D:
				push de	
				ifdef BuildSAM
					ld d,3
					ld hl,3					;3=SuckOfDeath
					call DoFarCall			;Routine is in other bank
				else ;ZXN
					ifdef BuildMSX2
						ld bc,VramYpos_SuckOfDeath 
					else
						ld bc,SuckOfDeath3dTiles
					endif
					ld hl,SuckOfDeath3dTileMap
				
					ld iyh,12	;X
					ld a,0		;Y
					call	ShowTileMapAltPosQtr	;HL=Tilemap BC=Tile Data IYH=X	A=Y 
				endif										;E=Height ;D=Wid
				call DoSet3D				;Set correct 3d mode
			endif
		endif
	endif
		
		ld a,0					;Zpos
		ld hl,&0B0E				;X,Y
		call LocateXYZ			;Show lines of gameover message

		ld hl,txtGameover1
		call PrintString

		ld de,&0B0F
		ld hl,txtGameover2
		call LocateandPrint

		ld de,&0B10
		ld hl,txtGameover3
		call LocateandPrint

		ld de,&0D11
		ld hl,txtGameover4
		call LocateandPrint


		ld hl,Score
		ld de,HiScore
		ld b,4
		call BCD_Cp				;Check if we have a new highscore
		jr nc,GameOverNoScore	;Jump if No highscore
		
;New Highscore
		ld de,&0C13
		ld hl,txtNewHiscore			
		call LocateandPrint
		
;Transfer score to highscore
		ld bc,4
		ld hl,Score
		ld de,HiScore
		ldir					;update the highscore

GameOverNoScore:
		ifdef BuildSMS
	;Show Hiscore (for SMS)
			ld a,0
			ld hl,&1017
			call LocateXYZ
			ld de,HiScore		
			ld b,4
			call BCD_Show
			
			ld de,&1016
			ld hl,txtHiScore
			call LocateandPrint
		endif
		
		call DoPalFadeInQ		;Fade in
	pop af
	call SetPalette

GameOverWait:
	ld bc,4000
	call PauseABC	
	
	call DoPalFadeOutQ
	xor a						;Disable speech on menu
	jp DrawTitleScreen



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

;		Set up a new level

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


InitLevel:
	call DoPalFadeOutQ			;Fade to black

	
;New level sound	
	ld hl,SndNewLevel			;Sound seq
	ld a,255					;Priority
	call PlaySoundSeq

;Clear sprites
	ld de,SpriteArray			;Clear sprites
	ld bc,SpriteArrayEnd-SpriteArray-1
	call Cldir

;Powerup
	ld hl,DefPowerup			;Collectable power up
	ld de,SprPowerup			
	Call DefineSprite			;Copy def from hl to DE
	
;Gun (HUD)
	ld hl,DefGun				;Gun icon in HUD
	ld de,SprGun
	Call DefineSprite			;Copy def from hl to DE

	ld iy,SprGun
	ld a,(GunNum)
	add GunSprite
	ld (IY+O_SprNum),a
	call DefineSpriteIY			;Set up sprite for selected gun

;Mountain (Reused for all background mountains)
	ld hl,DefMountain
	ld de,SprMountain
	Call DefineSprite			;Mountains (one sprite only)

;Moon
	ld hl,DefMoon
	ld de,SprMoon
	Call DefineSprite			;Moon (Also acts as timer)


;Clouds
	ld hl,DefCloud				;Define clouds
	ld de,sprClouds
	ld b,CloudCount
	Call DefineSprites

	ld iy,sprClouds+O_Xpos
	ld de,&0AF6
	ld b,CloudCount
	call SetSpritesRandomN		;Allocate Random Hpos

	ld iy,sprClouds
	ld de,&0004
	ld b,CloudCount
	call SetSpritesRandomDepth	;Set random YZ pos


;Grass
	ld hl,DefGrass
	ld de,sprGrass
	ld b,GrassCount 
	Call DefineSprites			;Set Grass sprites

	ld iy,sprGrass+O_Xpos
	ld de,&0AF6
	ld b,GrassCount 
	call SetSpritesRandomN		;Allocate Random Xpos

	ld iy,sprGrass
	ld de,&0508
	ld b,GrassCount
	call SetSpritesRandomDepth	;Set Random YZ pos

;Rock
	ld hl,DefRock
	ld de,SprRock
	ld b,RockCount  
	Call DefineSprites			;Define rocks

	ld iy,SprRock+O_Xpos
	ld de,&0AF6
	ld b,RockCount  
	call SetSpritesRandomN		;Allocate random Xpos
	
	ld iy,SprRock
	ld de,&0508
	ld b,RockCount
	call SetSpritesRandomDepth	;Set Random YZ pos


;Level Item init
	ld a,(LevelNum)
	and %00001111			;Level defs repeat every 16
	rlca
	ld c,a
	ld b,0					;2 bytes per def

	ld hl,LevelList
	add hl,bc
	ld e,(hl)				;Get level def in DE
	inc hl
	ld d,(hl)
	inc hl
	push de
	pop ix					;Shift to IX


	ld iy,LevelSequence
	ld de,SprEnemies		;Destination
	ld b,4					;4 kinds of enemy
InitLevelEnemies:
	push bc
		ld l,(iy)			;Source
		inc iy
		ld h,(iy)
		inc iy

		ld b,(ix)			;Count
		inc ix
		Call DefineSprites	;Copy Enemy Def
	pop bc
	djnz InitLevelEnemies	;Repeat


;Bonus Coronas
	ld hl,DefCorona
	ld a,(LevelNum)
	and %00100000
	rlca
	rlca
	rlca
	rlca	;%00000010
	ld b,a			;Bonus Coronas on later levels
	Call DefineSprites

;Bonus Basic Bat
	ld hl,DefBat1
	ld a,(LevelNum)
	and %00010000
	rrca
	rrca
	rrca	;%00000010
	ld b,a					;Bonus basic bats
	Call DefineSprites

;Bonus Gobbey
	ld a,(LevelNum)
	and %10000000 	;*2
	ld hl,DefBat2

	rlca	
	rlca	;%00000010
	ld b,a					;Bonus Gobbey bats
	Call DefineSprites
	

;Set Game speed
	ld e,(Ix)				;Moon Speed
	inc ix
	ld d,(Ix)				;Game Speed
	inc ix
	
	ld a,(LevelNum)
	and %11000000
	jr z,NoLevelSpeedup		;Speedup on hard levels
LevelSpeedUpAgain:
	srl d					;Game Speed
	scf
	rl e					;Moon Speed
	sub %01000000
	jr nz,LevelSpeedUpAgain
NoLevelSpeedup:
	ld a,d
	ld (GameSpeed),a		;Set game speed

	ld a,e					;Slow down moon (longer level)
	ld (SprMoon+O_AnimTick),a


;Enemy spawn time	
	ld iy,SprEnemies+O_Life
	ld de,&040C					;Initial spawn time
	ld b,EnemyCount
	call SetSpritesRandomN		;Random spawn time

	
;Init Scroll direction
	ld a,16						;Left
	ld (ScreenEdgeSpawnPos),a	;Default spawn pos

	ld a,(LevelNum)
	and %00000001	;Flip the movement dir (Every other level)
	jr nz,NoXFlip

;Do the flip
	ld iy,SpriteArray
	ld b,SpriteDataCount 
	ld de,SpriteDataSize

FlipAgain:
	ld a,(iy+O_MoveX)
	neg						;Flip horizontal move
	ld (iy+O_MoveX),a

	add iy,de
	djnz FlipAgain			;Move to next object
	
	ld a,(SprMoon+O_Xpos)
	sub VscreenMinX		
	ld b,a
	ld a,VscreenMinX+VscreenWid		;Flip moon pos
	sub b
	ld (SprMoon+O_Xpos),a

	ld a,VscreenMinX+VscreenWid+16
	ld (ScreenEdgeSpawnPos),a		;Set spawn pos

NoXFlip:

	ifdef BuildSMS
		call DefineGameTiles		;Set Game Tiles on SMS
	endif

	ld a,0			;Z
	ld hl,&1008		;X,Y
	call LocateXYZ					

	ld hl,TxtNight
	call PrintString				;Show Night (Level)
	
	ld a,(LevelNum)
	inc a
	call ShowDecimal

	call DoPalFadeInQ				;Fade in
	
	ld bc,1000
	call PauseABC					;Wait a bit
	
	call DoPalFadeOutQ				;Fade out
	ret
	
PauseABC:				;Delay for 255*BC
	push bc
		ld A,255
		call PauseA
		call UpdateSoundSequence
	pop bc
	dec bc
	ld a,b
	or c
	jr nz,PauseABC
	ret

PauseA:
	dec a
	ifndef buildsms
		nop
		nop				;SMS interrupt handler is heavy!
		nop
	endif
	nop
	jr nz,PauseA
	ret

	