Games with zero External Media

Miscellaneous Forums/Blitz Showcase/Games with zero External Media

OK.. so for last couple of weeks Ive been playing with the idea of creating a simple framework to allow games to be written using no external media.
Ive come up with what Im calling Block games. The graphics are very low res (160x120) zoomed x4 for display purposes. 16 colour fixed pallette. A couple of methods allow simple sounds to be generated.
I knocked up a space invaders game to try it out and it's damm fun writing it. It still needs a bit of work but it gives a good idea of what its all about.
anyways.. in case anyone is interested.. or bored.. or curious.. or whatever here is the source ( like I said.. no external media :) ).

I would be interested if it works OK on the older machines out there. btw.. dont worry if you cant get more than 30FPS.. thats a fast as it's supposed to go !.

Oh yeah. Press F2 to smooth the graphics display. F1 to go back to square blocks. ESC will quit the app.
In the game. P to start a game. Cursor keys move the player. Space key fires a shot.

First the framework. Save this first file as TBlockGame.bmx.

SuperStrict

Import BRL.GLMax2D
Import BRL.PNGLoader
Import BRL.FreeAudioAudio
Import BRL.Random

' modules which may be required:
' Import BRL.BMPLoader

Const WAVE_SIN:Int = 0
Const WAVE_SQUARE:Int = 1

Const WAVE_TRUNCATE:Int = 0
Const WAVE_BOUNCE:Int = 4
Const WAVE_WRAP:Int = 8	

Type TBaseSprite
	Field xPos:Float
	Field yPos:Float
	Field lifeSpan:Int
	Field dead:Int
	Field layer:Int
	Field xMove:Float
	Field yMove:Float
	
	Method Draw(canvas:TPixmap) Abstract

	Method Update()
		xPos :+ xMove
		yPos :+ yMove
		If lifeSpan >= 0 Then 
			lifeSpan :- 1
			If lifeSpan < 0 Then 
				dead = True
			End If
		End If
	End Method
	
	Method SetMove ( newXMove:Float, newYMove:Float)
		xMove = newXMove
		yMove = newYMove
	End Method
	
End Type

Type TSprite Extends TBaseSprite
'	Field image:TPixel[]
	Field image:Int[]
	Field width:Int
	Field height:Int
	Field frame:Int
	Field transparent:Int
	
	Method Draw(canvas:TPixmap)
		Local xPix:Int
		Local yPix:Int 
		Local imagePos:Int
		Local colPos:Int
		Local xPosPix:Int
		Local yPosPix:Int
		
		imagePos = frame * ( width * height )
		For yPix = 0 To height - 1
			For xPix = 0 To width - 1
'				image[imagePos].xPos = xPos + xPix
'				image[imagePos].yPos = yPos + yPix
				xPosPix = xPos + xPix
				yPosPix = yPos + yPix
'				colPos = image[imagePos].colour * 3
				colPos = image[imagePos] * 3
				If colPos <> transparent Then
'					image[imagePos].Draw(canvas)
					If xPosPix >=0 And xPosPix <= 159 And yPosPix >= 0 And yPosPix <= 119 Then
						WritePixel(canvas,Ceil(xPosPix), Ceil(yPosPix), $ff Shl 24 | TBlockGame.colours[colPos] Shl 16 | TBlockGame.colours[colPos+1] Shl 8 | TBlockGame.colours[colPos+2])
					End If
				End If
				imagePos :+ 1
			Next
		Next				
	End Method 

End Type

Type TPixel Extends TBaseSprite
	Field colour:Int
	
	Method Draw(canvas:TPixmap)
		Local colPos:Int = colour*3

		If xPos >=0 And xPos <= 159 And yPos >= 0 And yPos <= 119 Then
			WritePixel(canvas,Ceil(xPos), Ceil(yPos), $ff Shl 24 | TBlockGame.colours[colPos] Shl 16 | TBlockGame.colours[colPos+1] Shl 8 | TBlockGame.colours[colPos+2])
		End If
	End Method
	
	Function CreatePixel:TPixel(xPos:Int,yPos:Int, colour:Int, lifeSpan:Int=0, layer:Int=0 )
		Local pixel:TPixel
		pixel = New TPixel
		pixel.xPos = xPos
		pixel.yPos = yPos
		pixel.colour = colour
		pixel.lifeSpan = lifeSpan
		pixel.layer = layer
		
		Return pixel
	End Function
End Type

Type TBlockGame
	Global colours:Int[] = [0,0,0, 128,0,0, 0,128,0, 0,0,128, 128,128,0, 0,128,128, 128,0,128, 128,128,128, 64,64,64, 255,0,0, 0,255,0, 0,0,255, 255,255,0, 0, 255,255, 255,0,255, 255,255,255]
	
	' charset = chars 32-126 =  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
	' characters are displayed in a 5*5 grid
	Global text:String[] = [..
	"00000 00000 00000 00000 00000", .. 
	"00100 00100 00100 00000 00100", ..
	"00000 01010 01010 00000 00000", .. 
	"01010 11111 01010 11111 01010", ..
	"01111 10100 01110 00101 11110", ..
	"10001 00010 00100 01000 10001", ..
	"01100 10010 01110 10011 01111", ..
	"00100 00100 00000 00000 00000", ..
	"00100 01000 01000 01000 00100", ..
	"00100 00010 00010 00010 00100", ..
	"10101 00100 11111 00100 10101", ..
	"00100 00100 11111 00100 00100", ..
	"00000 00000 00000 00010 00100", .. 
	"00000 00000 11111 00000 00000", .. 
	"00000 00000 00000 00000 00100", .. 
	"00001 00010 00100 01000 10000", .. 
..
	"01110 10001 10101 10001 01110", .. 
	"00100 01100 00100 00100 00100", .. 
	"11110 00001 01110 10000 11111", .. 
	"11110 00001 00110 00001 11110", .. 
	"10000 10000 10100 11111 00100", .. 
	"11111 10000 11110 00001 11110", .. 
	"01100 10000 11110 10001 01110", .. 
	"11111 00001 00010 00010 00010", .. 
	"01110 10001 01110 10001 01110", .. 
	"01110 10001 01111 00001 00110", .. 
..
	"00000 00100 00000 00100 00000", .. 
	"00000 00100 00000 00100 01000", .. 
	"00010 00100 01000 00100 00010", .. 
	"00000 11111 00000 11111 00000", .. 
	"01000 00100 00010 00100 01000", .. 
	"01110 00001 00110 00100 00100", .. 
	"01110 10001 10111 10001 01111", .. 
..
	"01110 10001 11111 10001 10001", .. 
	"11110 10001 11110 10001 11110", .. 
	"01110 10000 10000 10000 01110", .. 
	"11110 10001 10001 10001 11110", .. 
	"11111 10000 11110 10000 11111", .. 
	"11111 10000 11110 10000 10000", .. 
	"01110 10000 10011 10001 01111", .. 
	"10001 10001 11111 10001 10001", .. 
	"01110 00100 00100 00100 01110", .. 
	"11111 00001 00001 10001 01110", .. 
	"10010 10100 11000 10100 10010", .. 
	"10000 10000 10000 10000 11111", .. 
	"10001 11011 11011 10101 10101", .. 
	"10001 11001 10101 10011 10001", .. 
	"01110 10001 10001 10001 01110", .. 
	"11110 10001 11110 10000 10000", .. 
	"01110 10001 10001 10011 01111", .. 
	"11110 10001 11110 10010 10001", .. 
	"01111 10000 01110 00001 11110", .. 
..
	"11111 00100 00100 00100 00100", .. 
	"10001 10001 10001 10001 01111", .. 
	"10001 10001 01010 01010 00100", .. 
	"10101 10101 11011 11011 10001", .. 
	"10001 01010 00100 01010 10001", .. 
	"10001 01010 00100 00100 00100", .. 
	"11111 00010 00100 01000 11111", .. 
..
	"01110 01000 01000 01000 01110", .. 
	"10000 01000 00100 00010 00001", .. 
	"01110 00010 00010 00010 01110", .. 
	"00100 01010 10001 00000 00000", .. 
	"00000 00000 00000 00000 11111", .. 
	"00100 00010 00000 00000 00000", .. 
..
	"01110 10001 11111 10001 10001", .. 
	"11110 10001 11110 10001 11110", .. 
	"01110 10000 10000 10000 01110", .. 
	"11110 10001 10001 10001 11110", .. 
	"11111 10000 11110 10000 11111", .. 
	"11111 10000 11110 10000 10000", .. 
	"01110 10000 10011 10001 01111", .. 
	"10001 10001 11111 10001 10001", .. 
	"01110 00100 00100 00100 01110", .. 
	"11111 00001 00001 10001 01110", .. 
	"10010 10100 11000 10100 10010", .. 
	"10000 10000 10000 10000 11111", .. 
	"10001 11011 11011 10101 10101", .. 
	"10001 11001 10101 10011 10001", .. 
	"01110 10001 10001 10001 01110", .. 
	"11110 10001 11110 10000 10000", .. 
	"01110 10001 10001 10011 01111", .. 
	"11110 10001 11110 10010 10001", .. 
	"01111 10000 01110 00001 11110", .. 
..
	"11111 00100 00100 00100 00100", .. 
	"10001 10001 10001 10001 01111", .. 
	"10001 10001 01010 01010 00100", .. 
	"10101 10101 11011 11011 10001", .. 
	"10001 01010 00100 01010 10001", .. 
	"10001 01010 00100 00100 00100", .. 
	"11111 00010 00100 01000 11111", .. 
..
	"00110 01000 11000 01000 00110", .. 
	"00100 00100 00000 00100 00100", .. 
	"01100 00010 00011 00010 01100", .. 
	"00000 01000 10101 00010 00000", .. 
	"11111 11111 11111 11111 11111" .. 
	]
	
	Const Black:Int = 0
	Const Red:Int = 1
	Const Green:Int = 2
	Const Blue:Int = 3
	Const Yellow:Int = 4
	Const Cyan:Int = 5
	Const Magenta:Int = 6
	Const White:Int = 7

	Const Bright:Int = 8

	Field sprites:TList 
	Field spriteLayers:TList[]
	Field layerCount:Int

	Field SoundEffect:TSoundEffect
	Field canvas:TPixmap
	Field scrImage:TImage
	
	Method New()
		sprites = CreateList()
		InitLayers(1)
		
		soundEffect = New TSoundEffect
	End Method

	Method InitLayers(newlayerCount:Int)
		layerCount = newlayerCount
		spritelayers = New TList[layerCount] 
		Local layer:Int
		
		For layer = 0 To layerCount - 1
			spriteLayers[layer] = CreateList()
		Next
	End Method

	Method GameRun()
	
		Graphics 640,480,0,30
		SetScale 4,4
		SetBlend(solidblend)

		scrImage = CreateImage(160,120,1,0)

		GameInit()

		While Not KeyHit ( Key_ESCAPE )
			' F1 will smooth the displayed image. F1 will display the image normally
			If KeyHit(key_F1) Then
				scrImage = CreateImage(160,120)
			End If
			If KeyHit(key_F2) Then
				scrImage = CreateImage(160,120,1,0)
			End If
			
			canvas = LockImage(scrImage)
			ClearPixels(canvas)			
			
			Local sprite:TBaseSprite
			Local spriteLayer:TList

			For spriteLayer = EachIn spriteLayers
				For sprite = EachIn spriteLayer
					If Not sprite.dead Then
						sprite.Draw(canvas)
						sprite.Update()
					Else
						ListRemove(sprites, sprite)
						ListRemove(spriteLayer,sprite)
					End If
				Next
			Next
			
			Update()

			UnlockImage(scrImage)
'SetColor 255,255,255
			DrawImage(scrImage,0,0)

			Flip 
			If KeyHit(key_S) Then
				Local scrdump:TPixmap
				scrDump = GrabPixmap(0,0,640,480)
				SavePixmapPNG(scrDump,"ScrDump.png",5)
			End If


		Wend
	End Method

	Method WriteText(xPos:Int, yPos:Int, colour:Int, msg:String)
		Local msgPos:Int
		Local char:Int
		Local charX:Int
		Local charY:Int
		Local chardef:String
		
		For msgPos = 0 To Len(msg)-1
			char = msg[msgpos] & $7f

			If char < 32 Then 
				char = 127
			End If
			chardef = text[char-32]
			For charY = 0 To 4
				For charX = 0 To 4
					If chardef[charY*6 + charX] = Asc("1") Then
						Local colPos:Int = colour*3
						WritePixel(canvas,Ceil(xPos+msgPos*6+charX), Ceil(yPos+charY), $ff Shl 24 | colours[colPos] Shl 16 | colours[colPos+1] Shl 8 | colours[colPos+2])
					End If
				Next
			Next
		Next	
	End Method

	Method DrawPixel:TPixel(xPos:Int,yPos:Int, colour:Int, lifeSpan:Int=0, layer:Int=0 )
		Local pixel:TPixel
		
		pixel = TPixel.CreatePixel ( xPos, yPos, colour, lifeSpan, layer)

		ListAddLast(sprites, pixel)
		ListAddLast(spriteLayers[layer],pixel)
		Return pixel
	End Method

	Method MakeSprite:TSprite(width:Int, height:Int, frames:Int=1, lifeSpan:Int=-1, layer:Int=0 )
		Local sprite:TSprite
		sprite = New TSprite
'		sprite.image = New TPixel[width*height*frames]
		sprite.image = New Int[width*height*frames]
		sprite.width = width
		sprite.height = height
		sprite.lifeSpan = lifeSpan
		sprite.layer = layer
		ListAddLast(sprites, sprite)
		ListAddLast(spriteLayers[layer],sprite)
		Return sprite
	End Method
	
	Method CloneSprite:TSprite(sourceSprite:TSprite)
		Local sprite:TSprite
		sprite = New TSprite
		sprite.image = sourceSprite.image
		sprite.width = sourceSprite.width
		sprite.height = sourceSprite.height
		sprite.lifeSpan = sourceSprite.lifeSpan
		sprite.xPos = sourceSprite.xPos
		sprite.yPos = sourceSprite.yPos
		sprite.frame = sourceSprite.frame
		sprite.layer = sourceSprite.layer
		sprite.transparent = sourceSprite.transparent
		ListAddLast(sprites, sprite)
		ListAddLast(spriteLayers[sprite.layer],sprite)
		Return sprite
	End Method
		
	Method SetSpriteImage( sprite:TSprite, pixels:Int[], frame:Int=0)
		Local xPix:Int
		Local yPix:Int
		Local colour:Int
		Local pixelPos:Int
		Local colourPos:Int
		
		pixelPos = sprite.width*sprite.height*frame
		colourPos = 0
		For yPix = 0 To sprite.height - 1
			For xPix = 0 To sprite.width - 1
				colour = pixels[colourPos]		
'				sprite.image[pixelPos] = TPixel.CreatePixel(xPix,yPix,colour,-1)
				sprite.image[pixelPos] = colour
				pixelPos :+ 1
				colourPos :+ 1
			Next
		Next
	End Method
	
	Method SetSpritePixel ( sprite:TSprite, xpos:Int, ypos:Int, colour:Int, frame:Int=0)
'		sprite.image[sprite.width*ypos+xpos] = TPixel.CreatePixel(xpos,ypos,colour,-1)
		sprite.image[sprite.width*ypos+xpos] = colour
	End Method

	Method GetSpritePixel:Int ( sprite:TSprite, xpos:Int, ypos:Int, frame:Int=0)
'		Return sprite.image[sprite.width*ypos+xpos].colour
		Return sprite.image[sprite.width*ypos+xpos]
	End Method

	Method SetSpritePos ( sprite:TSprite, xPos:Int, yPos:Int )
		sprite.xPos = xPos
		sprite.yPos = yPos
	End Method

	Method SetSpriteLayer ( sprite:TSprite, newLayer:Int )
		ListRemove(sprites,sprite)
		ListRemove(spriteLayers[sprite.layer], sprite)
		sprite.layer = newLayer
		ListAddLast(sprites, sprite)
		ListAddLast(spriteLayers[sprite.layer],sprite)
	End Method		

	Method SetSpriteTransparent ( sprite:TSprite, newTransparent:Int )
		sprite.transparent = newTransparent
	End Method

	Method CheckSpriteCollision:Int(sprite1:TSprite, sprite2:TSprite )
		Local xMin:Int
		Local xMax:Int
		Local yMin:Int
		Local yMax:Int
		Local collided:Int
		
		' Find the largest minimum x pixel of the sprites
		If sprite1.xPos < sprite2.xPos Then
			xMin = sprite2.xPos
		Else
			xMin = sprite1.xPos
		End If
		
		' find the smallest maximum x pixel of the sprites 
		If sprite1.xPos + sprite1.width - 1 > sprite2.xPos + sprite2.width - 1 Then
			xMax = sprite2.xPos + sprite2.width - 1
		Else
			xMax = sprite1.xPos + sprite1.width - 1
		End If
		
		' If the overlapping area in the x axis is inside the sprites
		If xMin <= xMax Then

			' find the largest minimum y pixel of the sprites
			If sprite1.yPos < sprite2.yPos Then
				yMin = sprite2.yPos
			Else
				yMin = sprite1.yPos
			End If
			
			' find tge smallest maximum y pixel of the sprites
			If sprite1.yPos + sprite1.height - 1 > sprite2.yPos + sprite2.height - 1 Then
				yMax = sprite2.yPos + sprite2.height - 1
			Else
				yMax = sprite1.yPos + sprite1.height - 1
			End If

			' if the overlapping are in the y axis is inside the sprites
			If yMin <= yMax Then
				' Now we have a potential collision. We also know the 
				' area that we need to check for pixel collisions
				Local xPos:Int
				Local yPos:Int
				For xPos = xMin To xMax
					For yPos = yMin To yMax
'						If sprite1.image[(xPos-sprite1.xPos)+(yPos-sprite1.yPos)*sprite1.width].colour <> 0 Then
'							If sprite2.image[(xPos-sprite2.xPos)+(yPos-sprite2.yPos)*sprite2.width].colour <> 0 Then
						If sprite1.image[(xPos-sprite1.xPos)+(yPos-sprite1.yPos)*sprite1.width] <> 0 Then
							If sprite2.image[(xPos-sprite2.xPos)+(yPos-sprite2.yPos)*sprite2.width] <> 0 Then
								collided = True
							End If
						End If
					Next
				Next
			End If
		End If

		Return collided
	End Method

	Method GameInit()
		Init()
	End Method

	Method init() Abstract

	Method Update() Abstract

	Method SetColour(colourNum:Int)
		Local colPos:Int = colourNum*3
		SetColor ( colours[colPos], colours[colPos+1], colours[colPos+2] )
	End Method

	Method SetClsColour(colourNum:Int)
		Local colPos:Int = colourNum*3
		SetClsColor ( colours[colPos], colours[colPos+1], colours[colPos+2] )
	End Method
	
	Method AddSoundEffect(duration:Float, startFreq:Float, endFreq:Float, startVol:Float, endVol:Float, noise:Float, waveType:Int)
		soundEffect.AddBlock(duration, startFreq, endFreq, startVol, endVol, noise, waveType)
	End Method
	
	Method MakeSoundEffect:TSound()
		Local sound:TSound
		
		sound = soundEffect.MakeSound()
		soundEffect = New TSoundEffect
		
		Return sound
	End Method
End Type

Type TSoundEffect
	Field blocks:TList
	Field totalSamples:Int
	
	Method New()
		blocks = CreateList()
	End Method
	
	Method AddBlock(duration:Float, startFreq:Float, endFreq:Float, startVol:Float, endVol:Float, noise:Float, waveType:Int)
		Local block:TSoundEffectBlock
		block = TSoundEffectBlock.Create(duration, startFreq, endFreq, startVol, endVol,noise, waveType )
		totalSamples :+ duration*11025
		ListAddLast(blocks, block)
	End Method
	
	Method MakeSound:TSound()
		Local sample:TAudioSample
		Local currentBlock:TSoundEffectBlock
		Local startSample:Int
		Local endSample:Int
		Local samplesInBlock:Int
		
		Local freqAngle:Float	
		Local volume:Float
		Local multiplier:Float
		Local k:Float
		Local angle:Float
		Local currentFreq:Float
		
		Local noiseValue:Float
		Local waveType:Int
		Local writesample:Float
		
		startSample = 0
		endSample = 0 
		
		Local lastrandomnoise:Float

		sample = CreateAudioSample( totalSamples,11025,SF_MONO8 )
		For currentBlock = EachIn blocks
		
			noiseValue = currentBlock.noise/100

			waveType = currentBlock.waveType

			startSample = endSample
			samplesInBlock = currentBlock.duration * 11025
			endSample = startSample + samplesInBlock
			
			For k = startSample Until endSample
			
				multiplier = (k-startSample) / samplesInBlock
				volume = ( currentBlock.startvol - ( ( currentBlock.startvol-currentBlock.endvol ) * multiplier ) )  * 127.5 / 100
				
				currentFreq = currentBlock.startfreq + ( ( currentBlock.endfreq-currentBlock.startfreq ) * multiplier )
				freqAngle = currentFreq/11025*180

				angle :+ freqAngle
				angle = angle Mod 360
				
				writesample = Sin(angle)*volume

				If waveType & WAVE_SQUARE Then
					If writeSample > 0 Then
						writeSample = volume
					Else
						writeSample = -volume
					End If
				End If

				lastrandomnoise = Rnd(-noisevalue,noisevalue)
				writesample :+ lastrandomnoise*volume
				
				If waveType & WAVE_BOUNCE Then
					If writeSample > 127.5 Then
						writeSample :- ( writeSample - 127.5)
					Else
						If writeSample < -127.5 Then
							writeSample :- ( writeSample + 127.5 )
						End If
					End If
				Else
					If waveType & WAVE_WRAP Then
						If writeSample > 127.5 Then 
							writeSample :- 255
						End If
						If writeSample < -127.5 Then
							writeSample :+ 255
						End If
					Else					
						' Truncate the sound
						If writesample > 127.5 Then
							writesample = 127.5
						End If
						If writesample < -127.5 Then
							writesample = -127.5
						End If
					End If
				End If	
							
				writesample :+ 127.5
								
				sample.samples[k]=writesample
			Next
		Next
		
		Local sound:TSound=LoadSound( sample,False )
	
		Return sound

	End Method
End Type

Type TSoundEffectBlock
	Field duration:Float
	Field startFreq:Float
	Field endFreq:Float
	Field startVol:Float
	Field endVol:Float
	Field noise:Float
	Field waveType:Int
	
	Function Create:TSoundEffectBlock(duration:Float, startFreq:Float, endFreq:Float, startVol:Float, endVol:Float, noise:Float, waveType:Int)
		Local this:TSoundEffectBlock
		this = New TSoundEffectBlock
		this.duration = duration
		this.startFreq = startFreq
		this.endFreq = endFreq
		this.startVol = startVol
		this.endVol = endVol
		this.noise = noise
		this.waveType = waveType	
		Return this
	End Function
End Type


and here is the Invaders game. Save as BlockInvaders.bmx. Compile and run. :)

SuperStrict

Framework BRL.FreeAudioAudio
Import BRL.StandardIO
Import "TBlockGame.bmx"

Local game:invGame
game = New invGame
game.GameRun()

	Type TBomb
		Field sprite:TSprite
		Field speed:Int
		
		Function Create:TBomb(setSprite:TSprite, setSpeed:Int )
			Local bomb:TBomb
			bomb = New TBomb
			bomb.sprite = setSprite
			bomb.speed = setSpeed
			
			Return bomb
		End Function
	End Type

Type invGame Extends TBlockGame

	Const TOTAL_INVADERS:Int = 5*11

	Field gameStage:Int			' what stage the game is in ( title page, game play, end game etc )

	Field Invaders:TSprite[TOTAL_INVADERS]	' list of all the invaders
	Field InvaderValues:Int [TOTAL_INVADERS]	' points value of each invader
	Field invadersLeft:Int			' how many invaders are alive
	Field SoundInvader:TSound[4]	' invader movement makes four noises
	Field soundInvNum:Int			' current noise invaders will make
	Field currentInv:Int
	Field SoundInvExplosion:TSound		' the sound an invader makes when it is hit

	Field wavesCleared:Int			' number of waves currently cleared

	Field bombs:TList
	Field bombDropPause:Int

	Field invStage:Int				' invader stages ( build wave, move right, down, left )
	Field invEdgeHit:Int			' while a wave moves if an invaders reaches the side of the screen this is set
	Field invPause:Int				' a counter when pausing after a wave is destroyed and a new one is built

	Field UFO:TSprite
	Field UFOPause:Int
	Field UFOValue:Int
	Field UFOShowScore:Int
	Field shields:TSprite[4]		' each level contains this many shields
	Field SoundUFO:TSound
	Field SoundUFOExplosion:TSound
	Field SoundUFOTime:Int
	
	Field Player:TSprite			' the player
	Field playerLives:Int				' Number of lives the player has left
	Field soundPlayerDie:TSound
	Field playerExplosion:TSprite
	
	Field soundShot:TSound			' the sound a shot makes
	Field shot:TSprite				' the shot
	
	Field Ground:TSprite			' the line under the player

	Field score:Int
	Field highScore:Int

	Field stagePause:Int

	Const LAYER_SHIELD:Int = 0		' each type of sprite exists on different layers
	Const LAYER_INV:Int 	= 1
	Const LAYER_BOMBS:Int 	= 2
	Const LAYER_SHOTS:Int 	= 3
	Const LAYER_PLAYER:Int = 4

	Field moveInvX:Int				' current invader that is moving
	Field moveInvY:Int

	Method Init()
		Local xInv:Int
		Local yInv:Int
		
		' tell the graphics engine how many layers we require
		InitLayers(5)
		
		bombs = CreateList()
				
		' Now build the sounds 
		
		' create the shot sound		
		AddSoundEffect(.2,5000,1000,30,30,50,WAVE_SQUARE + WAVE_BOUNCE)
		AddSoundEffect(.2,1000,500,30,0,50,WAVE_SQUARE + WAVE_BOUNCE)

		soundShot = MakeSoundEffect()
		
		' create the invader movement sounds
		' create invader move sound 0
		AddSoundEffect(.1,190,290,100,100,0,WAVE_SQUARE)
		AddSoundEffect(.1,290,190,100,100,0,WAVE_SQUARE)
		soundInvader[0] = MakeSoundEffect()		

		' create invader move sound 1
		AddSoundEffect(.1,100,200,100,100,0,WAVE_SQUARE)
		AddSoundEffect(.1,200,100,100,100,0,WAVE_SQUARE)
		soundInvader[1] = MakeSoundEffect()		

		' create invader move sound 2
		AddSoundEffect(.1,160,260,100,100,0,WAVE_SQUARE)
		AddSoundEffect(.1,260,160,100,100,0,WAVE_SQUARE)
		soundInvader[2] = MakeSoundEffect()		

		' create invader move sound 3
		AddSoundEffect(.1,130,230,100,100,0,WAVE_SQUARE)
		AddSoundEffect(.1,230,130,100,100,0,WAVE_SQUARE)
		soundInvader[3] = MakeSoundEffect()		
		Local l:Int
		For l = 0 To 200 
			AddSoundEffect(.01,Rand(100,500),Rand(100,500),(100-l/2),(100-l/2),20,1+WAVE_BOUNCE)
		Next
		
		soundPlayerDie = MakeSoundEffect()

		' create the invader explode sound		
		AddSoundEffect(.1,1000,3000,30,30,25,WAVE_SQUARE + WAVE_BOUNCE)
		soundInvExplosion = MakeSoundEffect()

		' create the UFO move sound		
		AddSoundEffect(.25,2000,3500,30,30,25,WAVE_SQUARE + WAVE_BOUNCE)
		AddSoundEffect(.25,3500,2000,30,30,25,WAVE_SQUARE + WAVE_BOUNCE)
		soundUFO = MakeSoundEffect()

		' create UFO explosion Sound
		AddSoundEffect(.2,4000,4500,80,50,25,WAVE_BOUNCE)
		AddSoundEffect(.2,0,0,0,0,0,WAVE_BOUNCE)
		AddSoundEffect(.2,4000,4500,80,50,25,WAVE_BOUNCE)
		AddSoundEffect(.2,0,0,0,0,0,WAVE_BOUNCE)
		AddSoundEffect(.2,4000,4500,80,50,25,WAVE_BOUNCE)
		AddSoundEffect(.2,0,0,0,0,0,WAVE_BOUNCE)
		AddSoundEffect(.2,4000,4500,80,50,25,WAVE_BOUNCE)
		AddSoundEffect(.2,0,0,0,0,0,WAVE_BOUNCE)
		soundUFOExplosion = MakeSoundEffect()

		SetGameStage(0)	
		
		' The game assumes a UFO Srpite exists
		CreateUFO()
		UFO.dead = True
	End Method
	
	' this is the main loop for the game
	Method Update()

		CountFPS()
		writeText 0,114,RED+BRIGHT,"FPS:"+Int(fps)

		' Show score and Hi Score at the top
		WriteText(5,0,GREEN+BRIGHT,"SCORE")
		Local numStr:String
		numStr = "000000" + score
		numStr = numStr[Len(numStr)-6..]
		WriteText(40,0,GREEN+BRIGHT,numStr)
		WriteText(100,0,GREEN+BRIGHT,"HI")
		numStr = "000000" + highScore
		numStr = numStr[Len(numStr)-6..]
		WriteText(115,0,GREEN+BRIGHT,numStr)

		' Show lives at the bottom right
		WriteText(100,115,GREEN+BRIGHT,"LIVES")
		numStr = "00" + playerLives
		numStr = numStr[Len(numStr)-2..]
		WriteText(140,115,GREEN+BRIGHT,numStr)
				
		Select gameStage
			Case 0	' Intro Page
				Intro()
			Case 1	' Play Game
				GamePlay()
			Case 2
				PlayerDie()
			Case 3	' End Game
				GameEnd()
		End Select
	End Method
	
	Method Intro()
		
		WriteText(37,30,WHITE+BRIGHT,"Block Invaders")

		WriteText(35,60,WHITE+BRIGHT,"Press P to Play")

		WriteText(15,85,GREEN+BRIGHT,"Written By Quietbloke")
		WriteText(50,95,GREEN+BRIGHT,"Sept 2007")
		WriteText(5,105,GREEN+BRIGHT,"using BlockGame Framework")

				
		If KeyHit(key_P) Then			
			StartGame()
		End If
	End Method
	
	Method PlayerDie()
		UpdateUFO()
	
		stagePause :+ 1
		' draw the explosion pixele. Each has a limited lifespan
		If stagePause < 150 Then
			Local p:Int
			For p = 0 To 3
				drawpixel(player.Xpos - 4 + Rand(0,13),player.yPos-4+Rand(0,9),Rand(1,15),Rand(5,15))
			Next
		End If
		' After a while if the player has more lives then flash the player on the screen a few times
		If stagePause >150 And playerLives > 0 Then 
			player.frame = Ceil(stagePause / 10) Mod 2
		End If
		
		If stagePause > 200 Then
			If playerLives = 0 Then
				SetGameStage(3)
			Else
				SetGameStage(1)
			End If
		End If
	End Method

	Method GameEnd()
		stagePause :+ 1

		' The player can press P now to start a new game
		If KeyHit(key_P) Then			
			ClearGame()
			StartGame()
		End If
								
		If stagePause < 90 Or Ceil((stagePause-90)/10) Mod 2 Then
			If stagePause > 10 Then
				WriteText ( 60, 55, RED+BRIGHT,"G")
			End If
			If stagePause > 20 Then
				WriteText ( 66, 55, RED+BRIGHT,"A")
			End If
			If stagePause > 30 Then
				WriteText ( 72, 55, RED+BRIGHT,"M")
			End If
			If stagePause > 40 Then
				WriteText ( 78, 55, RED+BRIGHT,"E")
			End If
			If stagePause > 50 Then
				WriteText ( 90, 55, RED+BRIGHT,"O")
			End If
			If stagePause > 60 Then
				WriteText ( 96, 55, RED+BRIGHT,"V")
			End If
			If stagePause > 70 Then
				WriteText ( 102, 55, RED+BRIGHT,"E")
			End If
			If stagePause > 80 Then
				WriteText ( 108, 55, RED+BRIGHT,"R")
			End If
		End If
		
		If stagePause > 300 Then
			SetGameStage(0)

			ClearGame()			
		End If		
	End Method
	
	Method ClearGame()
		' remove the player and ground
		player.dead = True
		shot.dead = True
		ground.dead = True
		
		player = Null
		shot = Null
		ground = Null
		
		' delete all the invaders
		Local pos:Int
		For pos = 0 Until TOTAL_INVADERS
			If invaders[pos] <> Null
				invaders[pos].dead = True
				invaders[pos] = Null
			End If
		Next
		invadersLeft = 0
		
		For pos = 0 To 3
			If shields[pos] <> Null
				shields[pos].dead = True
				shields[pos] = Null
			End If
		Next
		
		Local bomb:TBomb
		For bomb = EachIn bombs
			bomb.sprite.dead = True
			ListRemove(bombs,bomb)
		Next
	End Method
	
	Method StartGame()
		score = 0
		playerLives = 3
		invStage = 0
		wavesCleared = 0
		' Create the ground and player
		' create the player sprite
		Player = MakeSprite(5,5,2,-1,LAYER_PLAYER)
		SetSpriteImage(Player,[$0,$0,$0,$0,$0, $0,$0,$f,$0,$0, $0,$f,$f,$f,$0, $f,$f,$f,$f,$f, $f,$f,$f,$f,$f])
		SetSpritePos(Player,120,107)
		
		' create a shot
		CreateShot()
		
		' create the ground			
		Local xInv:Int
		ground = MakeSprite(158,1,1,-1,LAYER_SHIELD)
		SetSpritePos(Ground,1,113)
		For xInv = 0 To 157
			SetSpritePixel(ground,xInv,0,RED+BRIGHT)
		Next
		SetGameStage(1)
	End Method
	
	Method GamePlay()		

		' See if shot is dead then recreate it
		' a shot can die by either hitting something or it expires when it reaches the top of the screen
		If shot.dead Then
			CreateShot()
		End If

		CheckShotCollisions()
		
		CheckBombCollisions()
					
		MoveInvaders()
		
		MoveBombs()
	
		If invStage > 1 Then
			UFOPause :+ 1
			If UFOPause > 500 Then
				CreateUFO()
				UFOPause = 0
			End If
		End If

		UpdateUFO()

		' should we drop a bomb ?
		bombDropPause:- 1
		If bombDropPause <= 0 Then
			If invStage > 1 Then
				If Rnd() > .80 Then
					DropBomb()
				End If
			End If
		End If

		' move the player based on player input
		If KeyDown(Key_Left) Then
			Player.xPos :- 1
		End If
		
		If KeyDown(Key_Right) Then
			Player.xPos :+ 1
		End If
		
		' Make sure player remains in the play area
		If Player.xPos < 10 Then
			Player.XPos = 10
		End If
		If Player.XPos > 149-Player.width Then
			Player.XPos = 149-Player.width
		End If

		' If shot is not moving then place it wherever the player is
		If shot.yMove = 0 Then
			shot.yPos = player.yPos
			shot.xPos = player.xPos + 2
		End If
				
		' Is shot is not moving we now see if the player wishes to fire it
		If KeyHit(Key_SPACE) And shot.yMove = 0 Then
			PlaySound(soundShot)
			' set the lifespan so it will automatically expire as it hits the top of the screen
			shot.lifespan = 25
			' tell the shot sprite to start moving up the screen
			shot.SetMove(0,-4)
		End If

		If score > highScore Then
			highScore = score
		End If
			
	End Method

	Method CreateShot()
		Shot = MakeSprite(1,4,1,-1,LAYER_SHOTS)
		SetSpriteImage(shot,[$c,$c,$c,$c])
		shot.yPos = player.yPos
		shot.xPos = player.xPos + 2
	End Method

	Method MoveInvaders()
		Local inv:TSprite

		Local x:Int
		Local y:Int
		Local newInvStage:Int
		
		If invadersLeft = 0 And invStage > 1 Then
			InvStage = 0
		End If

		newInvStage = invStage
		
		Select invStage
			Case 0 ' pause
				If invPause = 0 Then
					CreateShields()
				End If
				invPause :+ 1
				If invPause > 100 Then
					invPause = 0
					newInvStage :+ 1
					currentInv = 0
				End If
			Case 1 ' build the wave
				Inv=makeSprite(6,5,2,-1,LAYER_INV)
				SetSpriteImage(Inv,[$0,$f,$f,$f,$f,$0, $f,$0,$f,$f,$0,$f, $0,$f,$f,$f,$f,$0, $0,$f,$0,$0,$f,$0, $f,$0,$0,$0,$0,$f],0)
				SetSpriteImage(Inv,[$0,$f,$f,$f,$f,$0, $f,$0,$f,$f,$0,$f, $0,$f,$f,$f,$f,$0, $0,$f,$0,$0,$f,$0, $0,$f,$0,$0,$f,$0],1)
				
				y = (currentInv / 11)
				x = (currentInv - y*11)
				SetSpritePos(Inv,x*12 + 10,107 - 40 - y*10) '  + 22)
				InvaderValues[currentInv] = Floor(y /2)* 10 + 10 

				invaders[currentInv] = inv

				invadersLeft :+ 1
				currentInv :+ 1
				If currentInv >= TOTAL_INVADERS Then
					newInvStage = 2
					currentInv = TOTAL_INVADERS
				End If

				invPause = 0
			Default

' If there is more than 1 invader left then find the next one that is going to move
				If invadersLeft > 0 Then
					Repeat
						currentInv :+ 1 
		
						If currentInv >= TOTAL_INVADERS
							currentInv = 0
							PlaySound(soundInvader[soundInvNum])
							soundInvNum :+ 1
							If soundInvNum > 3 Then
								soundInvNum = 0
							End If

							If invStage = 3 Then
								newInvStage = 4
								invEdgeHit = False
							Else
								If invStage = 5 Then
									newInvStage = 2
									invEdgeHit = False
								Else
									If invEdgeHit Then
										newInvStage :+ 1
										invEdgeHit = False
									End If
								End If
							End If
		
						End If
					Until invaders[currentInv] <> Null

					Select newInvStage
	
						Case 2 ' moving right
							x = 2
							y = 0
			
						Case 3 ' Moving right/down
							x = 0
							y = 5
							
						Case 4 ' Moving left
							x = -2
							y = 0
			
						Case 5 ' Moving left/Down
							x = 0
							y = 5						
					End Select				
	
					' move the invader
					invaders[currentInv].xPos :+ x
					invaders[currentInv].yPos :+ y
					invaders[currentInv].frame :+ 1
					If invaders[currentInv].frame > 1 Then
						invaders[currentInv].frame = 0
					End If 
		
					If invaders[currentInv].xPos > 147 Then
						invEdgeHit = True
					End If
					
					If invaders[currentInv].xPos < 6 Then
						invEdgeHit = True
					End If
					
					If invaders[currentInv].yPos >= 107 Then
						playerLives = 1 
						SetGameStage(2)
					End If
				End If


				Local shield:Int
				' Now the fancy bit.. we need to remove pixels from the shield that lie 
				' under the invader
				Local xPos2:Int
				Local yPos2:Int
				
				For shield = 0 To 3
					If checkSpriteCollision(shields[shield],invaders[currentInv]) Then
						For xPos2=0 To invaders[currentInv].width-1
							For yPos2=0 To invaders[currentInv].height-1
								Local xPos3:Int
								Local yPos3:Int
								xPos3 = invaders[currentInv].xPos-shields[shield].xPos+xPos2
								yPos3 = invaders[currentInv].yPos-shields[shield].yPos+yPos2
								If xPos3 >= 0 And xPos3 < shields[shield].width Then
									If yPos3 >= 0 And yPos3 < shields[shield].height Then
										SetSpritePixel(shields[shield],xPos3,yPos3,0)
									End If
								End If
							Next
						Next		
					End If
				Next	


		End Select
		
		invStage = newInvStage	
	End Method
	
	Method CheckShotCollisions()

		Local shield:Int
		Local inv:Int
		
		' check if shot has hit a shield
		For shield = 0 To 3
			If shields[shield] <> Null Then
				If CheckSpriteCollision(shot,shields[shield])
					' the shot moves more that 1 pixel at a time so we need to 
					' roll it back to find the exact position where it collided
					Repeat 
						shot.YPos :+ 1
					Until CheckSpriteCollision(shot,shields[shield]) = False
					shot.yPos :- 1
					ShieldExplosion(shields[shield],shot.xPos-2, shot.yPos-2)
					shot.dead = True
				End If
			End If
		Next
		
		' check each invader to see if the shot has hit it
		For inv = 0 Until TOTAL_INVADERS
			' Only do the check if the invader at this position still exists
			If invaders[inv] <> Null  Then
				If CheckSpriteCollision(shot,invaders[inv]) Then
					' delete the shot and the invader and reduce the invader count
					shot.dead = True
					invaders[inv].dead = True
					CreateInvaderExplosion(invaders[inv].xPos, invaders[inv].yPos)
					invaders[inv] = Null
					score :+ invaderValues[inv]	
					invadersLeft :- 1
					PlaySound(SoundInvExplosion)
				End If
			End If
		Next
		
		' Check the UFO to see if it has been hit
		If CheckSpriteCollision(shot,UFO) Then
			shot.dead = True
			UFO.dead = True
			UFOValue = 150 + Rand(0,6) * 50
			UFOShowScore = 75
			PlaySound(soundUFOExplosion)
		End If
	End Method
	
	Method CheckBombCollisions()
		' Check if a bomb has hit a shield
		Local bomb:TBomb
		Local shield:Int

		For bomb = EachIn bombs
			For shield = 0 To 3
				If shields[shield] <> Null Then
					If CheckSpriteCollision(bomb.sprite,shields[shield]) 
						Repeat
							bomb.sprite.YPos :- 1
						Until CheckSpriteCollision(bomb.sprite,shields[shield]) = False
						bomb.sprite.YPos :+ 1
						ShieldExplosion(shields[shield], bomb.sprite.xPos, bomb.sprite.yPos+2)
						bomb.sprite.dead = True
						ListRemove(bombs,bomb)
					End If
				End If
			Next
		Next

		' Check if bomb has hit the player
		For bomb = EachIn bombs
			If CheckSpriteCollision(bomb.sprite,player) 
				bomb.sprite.dead = True
				ListRemove(bombs,bomb)
				PlaySound(soundPlayerDie)
				SetGameStage(2)
			End If
		Next
	End Method
	
	Method MoveBombs()
		Local bomb:TBomb
		
		For bomb = EachIn bombs
			bomb.sprite.yPos :+ bomb.speed
			If bomb.sprite.yPos > 110 Then
				bomb.sprite.dead = True
				ListRemove(bombs,bomb)
			End If
		Next
	End Method

	Method InvSound()
		PlaySound(soundInvader[soundInvNum])
		soundInvNum :+ 1
		If soundInvNum > 3 Then
			soundInvNum = 0
		End If
	End Method

	Method CreateShields()
		Local shield:Int
		
		For shield = 0 To 3

			' Remove the existing shield sprite
			If shields[shield] <> Null Then
				shields[shield].dead = True
			End If
			shields[shield]=makeSprite(15,15,1,-1,LAYER_SHIELD)
			SetSpriteImage(shields[shield],[..
			$0,$0,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$0,$0, ..
			$0,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$0, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a,$a, ..
			$a,$a,$a,$0,$0,$0,$0,$0,$0,$0,$0,$0,$a,$a,$a, ..
			$a,$a,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$a,$a],0)

			SetSpritePos(shields[shield],15+shield*38 ,107-20)
		Next
		
	End Method
	
	Method CreateUFO()
		UFO = makeSprite(10,5,1,159-10,LAYER_INV)
		SetSpriteImage(UFO,[$0,$0,$f,$f,$f,$f,$f,$f,$0,$0, ..
			$0,$f,$f,$f,$f,$f,$f,$f,$f,$0, ..
			$f,$f,$0,$f,$0,$0,$f,$0,$f,$f, ..
			$0,$f,$f,$f,$f,$f,$f,$f,$f,$0, ..
			$0,$0,$f,$f,$f,$f,$f,$f,$0,$0],0)

		If Rnd() > .5 Then
			SetSpritePos(UFO,0,5)
			UFO.SetMove(1,0)
		Else
			SetSpritePos(UFO,159-UFO.width,5)
			UFO.SetMove(-1,0)
		End If
	End Method
		
	Method CreateInvaderExplosion(xPos:Int, yPos:Int)
		Local explosion:TSprite
		explosion=makeSprite(6,5,1,10,LAYER_SHIELD)
		SetSpriteImage(explosion,[$f,$0,$f,$0,$0,$f, $0,$f,$f,$0,$f,$0, $f,$f,$0,$0,$f,$f, $0,$f,$0,$f,$f,$0, $f,$0,$0,$f,$0,$f],0)
		SetSpritePos(explosion,xPos,yPos)
	End Method
	
	Method ShieldExplosion(shield:TSprite,xPos:Int,yPos:Int)
		Local explosion:TSprite
		explosion=makeSprite(5,5,1,10,LAYER_SHIELD)
		If Rnd() > .5 Then
			SetSpriteImage(explosion,[$0,$c,$0,$c,$c, $c,$c,$c,$c,$0, $0,$c,$c,$c,$0, $0,$c,$c,$c,$c, $c,$c,$0,$c,$0],0)
		Else
			SetSpriteImage(explosion,[$c,$c,$0,$c,$0, $0,$c,$c,$c,$c, $0,$c,$c,$c,$0, $c,$c,$c,$c,$0, $0,$c,$0,$c,$c],0)
		End If
		SetSpritePos(explosion,xPos,yPos)
		' Now the fancy bit.. we need to remove pixels from the shield that lie 
		' under the explosion sprite
		Local xPos2:Int
		Local yPos2:Int
		
		For xPos2=0 To explosion.width-1
			For yPos2=0 To explosion.height-1
				If GetSpritePixel(explosion,xPos2,yPos2) <> Black Then
					Local xPos3:Int
					Local yPos3:Int
					xPos3 = explosion.xPos-shield.xPos+xPos2
					yPos3 = explosion.yPos-shield.yPos+yPos2
					If xPos3 >= 0 And xPos3 < shield.width Then
						If yPos3 >= 0 And yPos3 < shield.height Then
							SetSpritePixel(shield,xPos3,yPos3,0)
						End If
					End If
				End If
			Next
		Next		
	End Method
	
	Method DropBomb()
		Local invader:Int
		If invadersLeft > 0 And invStage > 1 Then
			' pick a random invader
			invader = Rand(0,TOTAL_INVADERS-1)
			
			If invaders[invader] = Null
				Repeat
					invader:+ 1
					If invader > 5*11-1 Then
						invader = 0
					End If
				Until invaders[invader] <> Null 

			End If

			' go down the invaders until there is no invader below or bottom reached				
			Local invBelow:Int
			Local finished:Int
			
			Repeat
				invBelow = invader - 11
				If invBelow > 0 Then
					If invaders[invBelow] <> Null Then
						invader = invBelow
					Else
						finished = True
					End If
				Else
					finished = True
				End If
			Until finished = True
			
			' Create a bomb for the invader
			Local sprite:TSprite
			sprite=makeSprite(1,4,1,-1,LAYER_BOMBS)
			SetSpriteImage(sprite,[$f, $f, $f, $f],0)
			SetSpritePos(sprite,invaders[invader].xPos+2,invaders[invader].yPos+2)

			ListAddLast(bombs,TBomb.Create(sprite, Rand(1,2)))
			bombDropPause = 30
		End If
	End Method
	
	Method SetGameStage(newGameStage:Int)
		gameStage = newGameStage
		
		Select gameStage
			Case 1
				UFOPause = 0
			Case 2
				playerLives :- 1
				PlaySound(soundPlayerDie)
				' remove the shop and hide the player
				shot.dead = True
				player.frame = 1
			Case 3
				player.frame = 1
		End Select
		stagePause = 0
		FlushKeys()
	End Method
	
	Method UpdateUFO()
		If UFO.dead = False Then
			SoundUFOTime :-1 
			If SoundUFOTime < 0 Then
				PlaySound(SoundUFO)
				SoundUFOTime = 10
			End If
		End If
				
		If UFOShowScore > 0 Then
			WriteText( UFO.xPos, UFO.yPos, RED+BRIGHT, UFOValue)
			UFOShowScore :- 1
		End If
	End Method
End Type

Global fps:Float, fpst:Float,fpsc:Float

Function CountFPS:Float()
	If fpst < MilliSecs() Then
		fpst=MilliSecs()+1000
		fps = fpsc
		fpsc = 0
	Else
		fpsc = fpsc + 1
	End If
	Return fps
End Function


[ codebox ]
[ /codebox ]

aye.. yeah.. sorry... silly me. Fixed

Interesting idea - thanks for sharing.

Hmm, definitely very retro, but cool that you did this with no media. I was half expecting something kind of procedural though. ?

cheers.. I was going more for a sort of cut back Basic. Limited speed, graphics, sounds. That leaves me free to concentrate on gameplay.
As for the games you can write.. I was aiming more for simple stuff.. the sort of thing I might have seen printed in a computer magazine back when I were a lad.
In fact as a personnal goal I kind of had in the back of my head that the game source code cannot exceed 32K.

The resolution was set very low to start with as I didnt know what sort of performace I would get.
Also it is far easier to define the sprite graphics when the detail is so low.
As it happens my laptop even at its slowest CPU setting can reach almost 100fps. So.. there is a chance I can up the resolution.. maybe Ill try 320x240.. and of course the colour pallette is fixed but there is no reason or speed hit in increasing the number of colours or even allowing the pallette to be modified.

Anyway.. thanx for taking a look. Sorry about the 'procedural' disapointment :)

Hello.

If you look at the top of the blitz3d beginners area (link in sig) you'll see the community space invaders thread. The work done in there on producing programmed media was excellent (not pimping myself, by the way) and should give you an idea of what is capable of being produced in this way.

Goodbye.

Hi SoggyP.. how does your game generate sounds ?.. I cant seem to find code to do it... I only found the player shooting sound which seems to have the sound file stored in data.

I see what you mean about the graphics... hmmm... I currently only allow individual pixels to be set in the sprite images. Maybe I should extend the framework to allow lines to be drawn. Not really required now but if I up the resolution it would be handy.