Router example fixed

Router example fixed

Strict

Import mojo.graphics
Import bono

Class PlainScene Extends Partial
    Method OnRender:Void()
        Cls(0, 255, 0)
        DrawText("PLAIN", 0, 0)
    End

    Method OnTouchDown:Void(touch:TouchEvent)
        Router(director.handler).Goto("advanced")
    End
End

Class AdvancedScene Extends Partial Implements RouterEvents
    Method OnRender:Void()
        Cls(255, 0, 0)
        DrawText("ADVANCED", 0, 0)
    End

    Method OnTouchDown:Void(touch:TouchEvent)
        Router(director.handler).Goto("plain")
    End

    Method OnEnter:Void()
        Print("scene enter")
    End

    Method OnLeave:Void()
        Print("scene leave")
    End
End

Function Main:Int()
    Local router:Router = New Router()
    router.Add("plain", New PlainScene())
    router.Add("advanced", New AdvancedScene())
    router.Goto("plain")

    Local director:Director = New Director(640, 480)
    director.Run(router)

    Return 0
End

Strict

Import mojo.graphics
Import bono

Class PlainScene Extends NullObject
    Method OnRender:Void()
        Cls(0, 255, 0)
        DrawText("PLAIN", 0, 0)
    End

    Method OnTouchDown:Void(touch:TouchEvent)
        Router(director.handler).Goto("advanced")
    End
End

Class AdvancedScene Extends NullObject Implements RouterEvents
    Method OnRender:Void()
        Cls(255, 0, 0)
        DrawText("ADVANCED", 0, 0)
    End

    Method OnTouchDown:Void(touch:TouchEvent)
        Router(director.handler).Goto("plain")
    End

    Method OnEnter:Void()
        Print("scene enter")
    End

    Method OnLeave:Void()
        Print("scene leave")
    End
End

Function Main:Int()
    Local router:Router = New Router()
    router.Add("plain", New PlainScene())
    router.Add("advanced", New AdvancedScene())
    router.Goto("plain")

    Local director:Director = New Director(640, 480)
    director.Run(router)

    Return 0
End


Fixed FlxEmitter. Again? :)

Fixed FlxEmitter. Again? :)

Strict

Import reflection

Import flxextern
Import flxgroup
Import flxpoint
Import flxsprite
Import flxparticle
Import flxobject
Import flxg

Class FlxEmitter Extends FlxGroup

	Global __CLASS__:Object

	Field x:Float

	Field y:Float

	Field width:Float

	Field height:Float

	Field minParticleSpeed:FlxPoint

	Field maxParticleSpeed:FlxPoint

	Field particleDrag:FlxPoint

	Field minRotation:Float

	Field maxRotation:Float

	Field gravity:Float

	Field on:Bool

	Field frequency:Float

	Field lifespan:Float

	Field bounce:Float

	Field particleClass:ClassInfo

Private
	Field _quantity:Int

	Field _explode:Bool

	Field _timer:Float

	Field _counter:Int

	Field _point:FlxPoint

Public
	Method New(x:Float = 0, y:Float = 0, size:Float = 0)
		Super.New(size)
		Self.x = x
		Self.y = y
		width = 0
		height = 0
		minParticleSpeed = New FlxPoint(-100, -100)
		maxParticleSpeed = New FlxPoint(100, 100)
		minRotation = -360
		maxRotation = 360
		gravity = 0
		particleClass = Null
		particleDrag = New FlxPoint()
		frequency = 0.1
		lifespan = 3
		bounce = 0
		_quantity = 0
		_counter = 0
		_explode = True
		on = False
		_point = New FlxPoint()
	End Method

	Method Destroy:Void()
		minParticleSpeed = Null
		maxParticleSpeed = Null
		particleDrag = Null
		particleClass = Null
		_point = Null

		Super.Destroy()
	End Method

	Method MakeParticles:FlxEmitter(graphic:String, quantity:Int = 50, backedRotations:Int = 16, multiple:Bool = False, collide:Float = 0)
		MaxSize = quantity

		Local totalFrames:Int = 1

		If (multiple) Then
			Local sprite:FlxSprite = New FlxSprite()
			sprite.LoadGraphic(graphic, True)
			totalFrames = sprite.frames
			sprite.Destroy()
		End If

		Local randomFrame:Int
		Local particle:FlxParticle
		Local i:Int = 0

		While (i < quantity)
			If (particleClass = Null) Then
				particle = New FlxParticle()
			Else
				particle = FlxParticle(particleClass.NewInstance())
			End If

			If (multiple) Then
				randomFrame = FlxG.Random() * totalFrames

				If (backedRotations > 0) Then
					particle.LoadRotatedGraphic(graphic, backedRotations, randomFrame)
				Else
					particle.LoadGraphic(graphic, True)
					particle.Frame = randomFrame
				End If
			Else
				If (backedRotations > 0) Then
					particle.LoadRotatedGraphic(graphic, backedRotations)
				Else
					particle.LoadGraphic(graphic)
				End If
			End If

			If (collide > 0) Then
				particle.width *= collide
				particle.height *= collide
				particle.CenterOffsets()
			Else
				particle.allowCollisions = FlxObject.NONE
			End If

			particle.exists = False
			Add(particle)

			i += 1
		Wend

		Return Self
	End Method

	Method Update:Void()
		If (on) Then
			If (_explode) Then
				on = False

				Local i:Int = 0
				Local l:Int = _quantity

				If (l <= 0 Or l > Length) Then
					l = Length
				End If

				While (i < l)
					EmitParticle()
					i += 1
				Wend

				_quantity = 0
			Else
				_timer += FlxG.Elapsed

				While (frequency > 0 And _timer > frequency And on)
					_timer -= frequency
					EmitParticle()

					_counter += 1
					If (_quantity > 0 And _counter >= _quantity) Then
						on = False
						_quantity = 0
					End If
				Wend
			End If
		End If

		Super.Update()
	End Method

	Method Kill:Void()
		on = False
		Super.Kill()
	End Method

	Method Start:Void(explode:Bool = True, lifespan:Float = 0, frequency:Float = .1, quantity:Int = 0)
		Revive()
		visible = True
		on = True

		_explode = explode
		Self.lifespan = lifespan
		Self.frequency = frequency
		_quantity += quantity

		_counter = 0
		_timer = 0
	End Method

	Method EmitParticle:Void()
		Local particle:FlxParticle = FlxParticle(Recycle(FlxParticle.__CLASS__))
		particle.lifespan = lifespan
		particle.elasticity = bounce
		particle.Reset(x - (particle.width Shr 1) + FlxG.Random() * width, y - (particle.height Shr 1) + FlxG.Random() * height)
		particle.visible = True

		If (minParticleSpeed.x <> maxParticleSpeed.x) Then
			particle.velocity.x = minParticleSpeed.x + FlxG.Random() * (maxParticleSpeed.x - minParticleSpeed.x)
		Else
			particle.velocity.x = minParticleSpeed.x
		End If

		If (minParticleSpeed.y <> maxParticleSpeed.y) Then
			particle.velocity.y = minParticleSpeed.y + FlxG.Random() * (maxParticleSpeed.y - minParticleSpeed.y)
		Else
			particle.velocity.y = minParticleSpeed.y
		End If

		particle.acceleration.y = gravity

		If (minRotation <> maxRotation) Then
			particle.angularVelocity = minRotation + FlxG.Random() * (maxRotation - minRotation)
		Else
			particle.angularVelocity = minRotation
		End If

		If (particle.angularVelocity <> 0) Then
			particle.angle = FlxG.Random()*360 - 180
		End If

		particle.drag.x = particleDrag.x
		particle.drag.y = particleDrag.y

		particle.OnEmit()
	End Method

	Method SetSize:Void(width:Int, height:Int)
		Self.width = width
		Self.height = height
	End Method

	Method SetXSpeed:Void(min:Float = 0, max:Float = 0)
		minParticleSpeed.x = min
		maxParticleSpeed.x = max
	End Method

	Method SetYSpeed:Void(min:Float = 0, max:Float = 0)
		minParticleSpeed.y = min
		maxParticleSpeed.y = max
	End Method

	Method SetRotation:Void(min:Float = 0, max:Float = 0)
		minRotation = min
		maxRotation = max
	End Method

	Method At:Void(object:FlxObject)
		object.GetMidpoint(_point)
		x = _point.x - (width Shr 1)
		y = _point.y - (height Shr 1)
	End Method

End Class

Strict

Import reflection

Import flxextern
Import flxgroup
Import flxpoint
Import flxsprite
Import flxparticle
Import flxobject
Import flxg

Class FlxEmitter Extends FlxGroup

	Global ClassObject:Object

	Field x:Float

	Field y:Float

	Field width:Float

	Field height:Float

	Field minParticleSpeed:FlxPoint

	Field maxParticleSpeed:FlxPoint

	Field particleDrag:FlxPoint

	Field minRotation:Float

	Field maxRotation:Float

	Field gravity:Float

	Field on:Bool

	Field frequency:Float

	Field lifespan:Float

	Field bounce:Float

	Field particleClass:ClassInfo

Private
	Field _quantity:Int

	Field _explode:Bool

	Field _timer:Float

	Field _counter:Int

	Field _point:FlxPoint

Public
	Method New(x:Float = 0, y:Float = 0, size:Float = 0)
		Super.New(size)
		Self.x = x
		Self.y = y
		width = 0
		height = 0
		minParticleSpeed = New FlxPoint(-100, -100)
		maxParticleSpeed = New FlxPoint(100, 100)
		minRotation = -360
		maxRotation = 360
		gravity = 0
		particleClass = Null
		particleDrag = New FlxPoint()
		frequency = 0.1
		lifespan = 3
		bounce = 0
		_quantity = 0
		_counter = 0
		_explode = True
		on = False
		_point = New FlxPoint()
	End Method

	Method Destroy:Void()
		minParticleSpeed = Null
		maxParticleSpeed = Null
		particleDrag = Null
		particleClass = Null
		_point = Null

		Super.Destroy()
	End Method

	Method MakeParticles:FlxEmitter(graphic:String, quantity:Int = 50, backedRotations:Int = 16, multiple:Bool = False, collide:Float = 0)
		MaxSize = quantity

		Local totalFrames:Int = 1

		If (multiple) Then
			Local sprite:FlxSprite = New FlxSprite()
			sprite.LoadGraphic(graphic, True)
			totalFrames = sprite.frames
			sprite.Destroy()
		End If

		Local randomFrame:Int
		Local particle:FlxParticle
		Local i:Int = 0

		While (i < quantity)
			If (particleClass = Null) Then
				particle = New FlxParticle()
			Else
				particle = FlxParticle(particleClass.NewInstance())
			End If

			If (multiple) Then
				randomFrame = FlxG.Random() * totalFrames

				If (backedRotations > 0) Then
					particle.LoadRotatedGraphic(graphic, backedRotations, randomFrame)
				Else
					particle.LoadGraphic(graphic, True)
					particle.Frame = randomFrame
				End If
			Else
				If (backedRotations > 0) Then
					particle.LoadRotatedGraphic(graphic, backedRotations)
				Else
					particle.LoadGraphic(graphic)
				End If
			End If

			If (collide > 0) Then
				particle.width *= collide
				particle.height *= collide
				particle.CenterOffsets()
			Else
				particle.allowCollisions = FlxObject.NONE
			End If

			particle.exists = False
			Add(particle)

			i += 1
		Wend

		Return Self
	End Method

	Method Update:Void()
		If (on) Then
			If (_explode) Then
				on = False

				Local i:Int = 0
				Local l:Int = _quantity

				If (l <= 0 Or l > Length) Then
					l = Length
				End If

				While (i < l)
					EmitParticle()
					i += 1
				Wend

				_quantity = 0
			Else
				_timer += FlxG.Elapsed

				While (frequency > 0 And _timer > frequency And on)
					_timer -= frequency
					EmitParticle()

					_counter += 1
					If (_quantity > 0 And _counter >= _quantity) Then
						on = False
						_quantity = 0
					End If
				Wend
			End If
		End If

		Super.Update()
	End Method

	Method Kill:Void()
		on = False
		Super.Kill()
	End Method

	Method Start:Void(explode:Bool = True, lifespan:Float = 0, frequency:Float = .1, quantity:Int = 0)
		Revive()
		visible = True
		on = True

		_explode = explode
		Self.lifespan = lifespan
		Self.frequency = frequency
		_quantity += quantity

		_counter = 0
		_timer = 0
	End Method

	Method EmitParticle:Void()
		Local particle:FlxParticle = FlxParticle(Recycle(FlxParticle.__CLASS__))
		particle.lifespan = lifespan
		particle.elasticity = bounce
		particle.Reset(x - (particle.width Shr 1) + FlxG.Random() * width, y - (particle.height Shr 1) + FlxG.Random() * height)
		particle.visible = True

		If (minParticleSpeed.x <> maxParticleSpeed.x) Then
			particle.velocity.x = minParticleSpeed.x + FlxG.Random() * (maxParticleSpeed.x - minParticleSpeed.x)
		Else
			particle.velocity.x = minParticleSpeed.x
		End If

		If (minParticleSpeed.y <> maxParticleSpeed.y) Then
			particle.velocity.y = minParticleSpeed.y + FlxG.Random() * (maxParticleSpeed.y - minParticleSpeed.y)
		Else
			particle.velocity.y = minParticleSpeed.y
		End If

		particle.acceleration.y = gravity

		If (minRotation <> maxRotation) Then
			particle.angularVelocity = minRotation + FlxG.Random() * (maxRotation - minRotation)
		Else
			particle.angularVelocity = minRotation
		End If

		If (particle.angularVelocity <> 0) Then
			particle.angle = FlxG.Random()*360 - 180
		End If

		particle.drag.x = particleDrag.x
		particle.drag.y = particleDrag.y

		particle.OnEmit()
	End Method

	Method SetSize:Void(width:Int, height:Int)
		Self.width = width
		Self.height = height
	End Method

	Method SetXSpeed:Void(min:Float = 0, max:Float = 0)
		minParticleSpeed.x = min
		maxParticleSpeed.x = max
	End Method

	Method SetYSpeed:Void(min:Float = 0, max:Float = 0)
		minParticleSpeed.y = min
		maxParticleSpeed.y = max
	End Method

	Method SetRotation:Void(min:Float = 0, max:Float = 0)
		minRotation = min
		maxRotation = max
	End Method

	Method At:Void(object:FlxObject)
		object.GetMidpoint(_point)
		x = _point.x - (width Shr 1)
		y = _point.y - (height Shr 1)
	End Method

End Class


Add/Remove scene to all emitters before OnSceneEnter & -Leave

Strict

Private

Import sceneable
Import bono.kernel

Public

Class SceneManager
    Private

    Field scenes:StringMap<Sceneable> = New StringMap<Sceneable>()
    Field _current:Sceneable
    Field _currentName:String
    Field _previous:Sceneable
    Field _previousName:String

    Public

    Field appEmitter:AppEmitter
    Field keyEmitter:KeyEmitter = New KeyEmitter()
    Field touchEmitter:TouchEmitter = New TouchEmitter()

    Method New(fps:Int=AppEmitter.DEFAULT_FPS)
        appEmitter = New AppEmitter(fps)
        appEmitter.AddObserver(keyEmitter)
        appEmitter.AddObserver(touchEmitter)
    End

    Method New(appEmitter:AppEmitter)
        Self.appEmitter = appEmitter
        appEmitter.AddObserver(keyEmitter)
        appEmitter.AddObserver(touchEmitter)
    End

    Method Add:Void(name:String, scene:Sceneable)
        If scenes.Contains(name)
            Error("There is already a scene named: " + name)
        End

        scenes.Set(name, scene)
        scene.SetSceneManager(Self)
    End

    Method Get:Sceneable(name:String)
        If Not scenes.Contains(name)
            Error("Unknown scene name given: " + name)
        End

        Return scenes.Get(name)
    End

    Method Goto:Void(name:String)
        If name = _currentName Then Return

        ChangeCurrentAndPrevious(name)
        HandlePreviousLeave()
        HandleCurrentEnter()
    End

    Method current:Sceneable() Property
        Return _current
    End

    Method currentName:String() Property
        Return _currentName
    End

    Method previous:Sceneable() Property
        Return _previous
    End

    Method previousName:String() Property
        Return _previousName
    End

    Private

    Method ChangeCurrentAndPrevious:Void(name:String)
        _previous = _current
        _previousName = _currentName

        _current = Get(name)
        _currentName = name
    End

    Method HandlePreviousLeave:Void()
        If Not _previous Then Return

        If AppObserver(_previous) Then appEmitter.RemoveObserver(AppObserver(_previous))
        If KeyObserver(_previous) Then keyEmitter.RemoveObserver(KeyObserver(_previous))
        If TouchObserver(_previous) Then touchEmitter.RemoveObserver(TouchObserver(_previous))
        _previous.OnSceneLeave()
    End

    Method HandleCurrentEnter:Void()
        If AppObserver(_current) Then appEmitter.AddObserver(AppObserver(_current))
        If KeyObserver(_current) Then keyEmitter.AddObserver(KeyObserver(_current))
        If TouchObserver(_current) Then touchEmitter.AddObserver(TouchObserver(_current))
        _current.OnSceneEnter()
    End
End

Strict

Private

Import sceneable
Import bono.kernel

Public

Class SceneManager
    Private

    Field scenes:StringMap<Sceneable> = New StringMap<Sceneable>()
    Field _current:Sceneable
    Field _currentName:String
    Field _previous:Sceneable
    Field _previousName:String

    Public

    Field appEmitter:AppEmitter
    Field keyEmitter:KeyEmitter = New KeyEmitter()
    Field touchEmitter:TouchEmitter = New TouchEmitter()

    Method New(fps:Int=AppEmitter.DEFAULT_FPS)
        appEmitter = New AppEmitter(fps)
        appEmitter.AddObserver(keyEmitter)
        appEmitter.AddObserver(touchEmitter)
    End

    Method New(appEmitter:AppEmitter)
        Self.appEmitter = appEmitter
        appEmitter.AddObserver(keyEmitter)
        appEmitter.AddObserver(touchEmitter)
    End

    Method Add:Void(name:String, scene:Sceneable)
        If scenes.Contains(name)
            Error("There is already a scene named: " + name)
        End

        scenes.Set(name, scene)
        scene.SetSceneManager(Self)
    End

    Method Get:Sceneable(name:String)
        If Not scenes.Contains(name)
            Error("Unknown scene name given: " + name)
        End

        Return scenes.Get(name)
    End

    Method Goto:Void(name:String)
        If name = _currentName Then Return

        ChangeCurrentAndPrevious(name)
        HandlePreviousLeave()
        HandleCurrentEnter()
    End

    Method current:Sceneable() Property
        Return _current
    End

    Method currentName:String() Property
        Return _currentName
    End

    Method previous:Sceneable() Property
        Return _previous
    End

    Method previousName:String() Property
        Return _previousName
    End

    Private

    Method ChangeCurrentAndPrevious:Void(name:String)
        _previous = _current
        _previousName = _currentName

        _current = Get(name)
        _currentName = name
    End

    Method HandlePreviousLeave:Void()
        If Not _previous Then Return

        _previous.OnSceneLeave()
        If AppObserver(_previous) Then appEmitter.RemoveObserver(AppObserver(_previous))
        If KeyObserver(_previous) Then keyEmitter.RemoveObserver(KeyObserver(_previous))
        If TouchObserver(_previous) Then touchEmitter.RemoveObserver(TouchObserver(_previous))
    End

    Method HandleCurrentEnter:Void()
        _current.OnSceneEnter()
        If AppObserver(_current) Then appEmitter.AddObserver(AppObserver(_current))
        If KeyObserver(_current) Then keyEmitter.AddObserver(KeyObserver(_current))
        If TouchObserver(_current) Then touchEmitter.AddObserver(TouchObserver(_current))
    End
End


Slightly altered the touch example.

Slightly altered the touch example.

Strict

Import diddy

Function Main:Int()
	game = new MyGame()
	Return 0
End Function

Global gameScreen:GameScreen

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		' enable touch for non-phones
		#If TARGET<>"ios" or TARGET<>"android"
			game.inputCache.MonitorTouch(True)
		#End
		gameScreen = new GameScreen
		gameScreen.PreStart()
		return 0
	End
End

Class GameScreen Extends Screen
	Const CIRCLE_RADIUS:Float = 20
	Const DEFAULT_LIFE:Float = 2000
	Const EPSILON:Float = 3
	Field x:Float[32]
	Field y:Float[32]
	Field vx:Float[32]
	Field vy:Float[32]
	Field life:Float[32]
	Field down:Bool[32]
	Field red:Int, green:Int, blue:Int
	Field useBigCircles:Bool

	Method New()
		name = "Game"
	End

	Method Start:Void()
		game.screenFade.Start(50, false)
	End

	Method Render:Void()
		' fill the background with an awful colour
		Cls(red, green, blue)
		' for each of the circles
		For Local i:Int = 0 Until life.Length
			' if we're touching the screen for this circle, or it has some life left
			If life[i]>0 Or down[i] Then
				' if we're touching the screen or its life is over half
				If down[i] Or life[i] > DEFAULT_LIFE/2 Then
					' draw at full alpha
					SetAlpha(1)
				Else
					' otherwise fade it out
					SetAlpha(life[i]/(DEFAULT_LIFE/2))
				End
				' draw a white circle and reset the alpha
				SetColor(255,255,255)
				If i=1 And useBigCircles Then
					DrawCircle(x[i],y[i],2*CIRCLE_RADIUS)
				Else
					DrawCircle(x[i],y[i],CIRCLE_RADIUS)
				End
				SetAlpha(1)
			End
		Next
		SetColor(255,255,255)
		DrawInfo()
	End

	Method DrawInfo:Void()
		DrawText("Diddy's Touch Test", 0, 0)
		DrawText("Press to move the circle and release to 'fling' it!", 0, 15)
		DrawText("Press down for a few seconds to change the background colour", 0, 30)
		DrawText("Do two touches to switch between big and small circles just for that fling", 0, 45)
		FPSCounter.Draw(0, 60)
	End

	Method Update:Void()
		If KeyHit(KEY_ESCAPE)
			game.screenFade.Start(50, true)
			game.nextScreen = game.exitScreen
		End
		' for each of the circles
		For Local i:Int = 0 Until life.Length
			' if we're not touching the screen for this circle and it has some life left
			If Not down[i] And life[i]>0 Then
				' slow down the velocity
				vx[i] *= 0.95
				vy[i] *= 0.95

				' move the circles
				x[i] += vx[i]*dt.frametime/1000
				y[i] += vy[i]*dt.frametime/1000

				' dodgy bouncing code
				If x[i]<0 Then
					x[i] *= -1
					vx[i] *= -1
				End
				If x[i]>SCREEN_WIDTH Then
					x[i] = 2*SCREEN_WIDTH - x[i]
					vx[i] *= -1
				End
				If y[i]<0 Then
					y[i] *= -1
					vy[i] *= -1
				End
				If y[i]>SCREEN_HEIGHT Then
					y[i] = 2*SCREEN_HEIGHT - y[i]
					vy[i] *= -1
				End
				If vx[i]*vx[i]+vy[i]*vy[i]<=EPSILON*EPSILON Then
					vx[i] = 0
					vy[i] = 0
				End

				' if it's stopped moving
				If vx[i]=0 And vy[i]=0 Then
					' decrease the life
					life[i] -= dt.frametime
					' clip it to 0
					If life[i] < 0 Then life[i] = 0
				End
			End
		Next
	End

	Method OnTouchHit:Void(x:Int, y:Int, pointer:Int)
		' if we touched, update the position of the circle for this pointer and mark it as down
		Self.x[pointer] = x
		Self.y[pointer] = y
		Self.vx[pointer] = 0
		Self.vy[pointer] = 0
		Self.down[pointer] = True
	End

	Method OnTouchReleased:Void(x:Int, y:Int, pointer:Int)
		' if we dragged, update the position of the circle for this pointer, and prepare it to fade out
		Self.x[pointer] = x
		Self.y[pointer] = y
		Self.down[pointer] = False
		Self.life[pointer] = DEFAULT_LIFE
	End

	Method OnTouchDragged:Void(x:Int, y:Int, dx:Int, dy:Int, pointer:Int)
		' if we dragged, update the position of the circle for this pointer
		Self.x[pointer] = x
		Self.y[pointer] = y
	End

	Method OnTouchFling:Void(releaseX:Int, releaseY:Int, velocityX:Float, velocityY:Float, velocitySpeed:Float, pointer:Int)
		' if we fling, update the position and velocity of the circle for this pointer
		Self.x[pointer] = releaseX
		Self.y[pointer] = releaseY
		Self.vx[pointer] = velocityX
		Self.vy[pointer] = velocityY
	End

	Method OnTouchLongPress:Void(x:Int, y:Int, pointer:Int)
		' if we long press, change the background colour to something random
		red = Rnd(0,255)
		green = Rnd(0,255)
		blue = Rnd(0,255)
		If red+green+blue < 192 Then
			red = Rnd(128,255)
			green = Rnd(128,255)
			blue = Rnd(128,255)
		End
	End

	Method OnTouchClick:Void(x:Int, y:Int, pointer:Int)
		' if we clicked with the second finger, switch between big and small circles just for that one
		If pointer = 1 Then useBigCircles = Not useBigCircles
	End
End

Strict

Import diddy

Function Main:Int()
	game = new MyGame()
	Return 0
End Function

Global gameScreen:GameScreen

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		gameScreen = new GameScreen
		gameScreen.PreStart()
		return 0
	End
End

Class GameScreen Extends Screen
	Const CIRCLE_RADIUS:Float = 20
	Const DEFAULT_LIFE:Float = 2000
	Const EPSILON:Float = 3
	Field x:Float[32]
	Field y:Float[32]
	Field vx:Float[32]
	Field vy:Float[32]
	Field life:Float[32]
	Field down:Bool[32]
	Field red:Int, green:Int, blue:Int
	Field useBigCircles:Bool

	Method New()
		name = "Game"
	End

	Method Start:Void()
		game.screenFade.Start(50, false)
	End

	Method Render:Void()
		Cls
		' fill the background with an awful colour
		SetColor(red,green,blue)
		DrawRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT)
		' for each of the circles
		For Local i:Int = 0 Until life.Length
			' if we're touching the screen for this circle, or it has some life left
			If life[i]>0 Or down[i] Then
				' if we're touching the screen or its life is over half
				If down[i] Or life[i] > DEFAULT_LIFE/2 Then
					' draw at full alpha
					SetAlpha(1)
				Else
					' otherwise fade it out
					SetAlpha(life[i]/(DEFAULT_LIFE/2))
				End
				' draw a white circle and reset the alpha
				SetColor(255,255,255)
				If i=1 And useBigCircles Then
					DrawCircle(x[i],y[i],2*CIRCLE_RADIUS)
				Else
					DrawCircle(x[i],y[i],CIRCLE_RADIUS)
				End
				SetAlpha(1)
			End
		Next
		SetColor(255,255,255)
		FPSCounter.Draw(0,0)
	End

	Method Update:Void()
		If KeyHit(KEY_ESCAPE)
			game.screenFade.Start(50, true)
			game.nextScreen = game.exitScreen
		End
		' for each of the circles
		For Local i:Int = 0 Until life.Length
			' if we're not touching the screen for this circle and it has some life left
			If Not down[i] And life[i]>0 Then
				' slow down the velocity
				vx[i] *= 0.95
				vy[i] *= 0.95

				' move the circles
				x[i] += vx[i]*dt.frametime/1000
				y[i] += vy[i]*dt.frametime/1000

				' dodgy bouncing code
				If x[i]<0 Then
					x[i] *= -1
					vx[i] *= -1
				End
				If x[i]>SCREEN_WIDTH Then
					x[i] = 2*SCREEN_WIDTH - x[i]
					vx[i] *= -1
				End
				If y[i]<0 Then
					y[i] *= -1
					vy[i] *= -1
				End
				If y[i]>SCREEN_HEIGHT Then
					y[i] = 2*SCREEN_HEIGHT - y[i]
					vy[i] *= -1
				End
				If vx[i]*vx[i]+vy[i]*vy[i]<=EPSILON*EPSILON Then
					vx[i] = 0
					vy[i] = 0
				End

				' if it's stopped moving
				If vx[i]=0 And vy[i]=0 Then
					' decrease the life
					life[i] -= dt.frametime
					' clip it to 0
					If life[i] < 0 Then life[i] = 0
				End
			End
		Next
	End

	Method OnTouchHit:Void(x:Int, y:Int, pointer:Int)
		' if we touched, update the position of the circle for this pointer and mark it as down
		Self.x[pointer] = x
		Self.y[pointer] = y
		Self.vx[pointer] = 0
		Self.vy[pointer] = 0
		Self.down[pointer] = True
	End

	Method OnTouchReleased:Void(x:Int, y:Int, pointer:Int)
		' if we dragged, update the position of the circle for this pointer, and prepare it to fade out
		Self.x[pointer] = x
		Self.y[pointer] = y
		Self.down[pointer] = False
		Self.life[pointer] = DEFAULT_LIFE
	End

	Method OnTouchDragged:Void(x:Int, y:Int, dx:Int, dy:Int, pointer:Int)
		' if we dragged, update the position of the circle for this pointer
		Self.x[pointer] = x
		Self.y[pointer] = y
	End

	Method OnTouchFling:Void(releaseX:Int, releaseY:Int, velocityX:Float, velocityY:Float, velocitySpeed:Float, pointer:Int)
		' if we fling, update the position and velocity of the circle for this pointer
		Self.x[pointer] = releaseX
		Self.y[pointer] = releaseY
		Self.vx[pointer] = velocityX
		Self.vy[pointer] = velocityY
	End

	Method OnTouchLongPress:Void(x:Int, y:Int, pointer:Int)
		' if we long press, change the background colour to something random
		red = Rnd(0,255)
		green = Rnd(0,255)
		blue = Rnd(0,255)
		If red+green+blue < 192 Then
			red = Rnd(128,255)
			green = Rnd(128,255)
			blue = Rnd(128,255)
		End
	End

	Method OnTouchClick:Void(x:Int, y:Int, pointer:Int)
		' if we clicked with the second finger, switch between big and small circles just for that one
		If pointer = 1 Then useBigCircles = Not useBigCircles
	End
End