Updated particle example for scale and rotation.

Strict

Import mojo
Import diddy

Global testScreen:ParticleTestScreen

Function Main:Int()
	game = New ParticleTestApp
	Return 0
End

Class ParticleTestApp Extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		drawFPSOn = True
		testScreen = New ParticleTestScreen
		testScreen.PreStart()
		Return 0
	End
End

Class ParticleTestScreen Extends Screen
	Field ps:ParticleSystem
	Field pg:ParticleGroup
	Field e:Emitter
	Field e2:Emitter
	Field f:Force
	Field speck:Image

	Field emitting:Bool
	Field rendering:Bool = True

	Method New()
		name = "Particle System Test"
		speck = LoadImage("speck.png",,Image.MidHandle)
		CreateSystem()
	End

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

	Method Render:Void()
		Cls
		If rendering Then ps.Render()
		SetColor(255,255,255)
	End

	Method DebugRender:Void()
		SetColor(255,255,255)
		SetAlpha(1)
#If TARGET="android" Or TARGET="ios" Then
		DrawText("Tap top half to toggle emitter, bottom half to toggle rendering",0,30)
#Else
		DrawText("Space: toggle emitter, R: toggle rendering",0,30)
#End
		DrawText("pg.AliveParticles="+pg.AliveParticles,0,50)
	End

	Method Update:Void()
		If KeyHit(KEY_SPACE) Then emitting = Not emitting
		If KeyHit(KEY_R) Then rendering = Not rendering

		If TouchHit() Then
			If TouchY() < SCREEN_HEIGHT2 Then
				emitting = Not emitting
			Else
				rendering = Not rendering
			End
		End
		If emitting Then
			e.EmitAt(3, SCREEN_WIDTH2, SCREEN_HEIGHT2+50)
		End
		ps.Update(dt.frametime)
		If KeyHit(KEY_ESCAPE) Then
			game.screenFade.Start(50, True)
			game.nextScreen = game.exitScreen
		End
	End

	Method CreateSystem:Void()
		ps = New ParticleSystem()

		pg = New ParticleGroup(5000) ' group of 5000 particles
		ps.Groups.Add(pg)

		f = New ConstantForce(0,-150) ' constant downward force of 150 pixels per second per second
		pg.Forces.Add(f)

		e = New Emitter
		e.SetParticleRGBInterpolated(255,0,0,0,255,0) ' fade from red to green
		e.Life = 3 ' lives for 3 seconds
		e.SetPolarVelocity(PI/2, PI/3, 250, 50) ' points up with a spread of 60 degrees
		e.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e.ParticleImage = speck
		e.SetParticleScale(1.5,2)
		e.Group = pg

		e2 = New Emitter
		e2.Life = 3 ' lives for 3 seconds
		e2.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e2.SetPolarVelocity(0,2*PI,50,10) ' points right with a spread of 360 degrees (a full circle)
		e2.SetParticleRGBInterpolated(0,255,255,0,255,0) ' fade from cyan to green
		e2.SetParticleScale(2,2)
		e2.SetParticleRotation(0, PI/8, 0, PI/2)
		e.AddDeathEmitter(e2, 0.05) ' 5% chance to fire off this emitter when a particle from other emitter dies
	End
End

Strict

Import mojo
Import diddy

Global testScreen:ParticleTestScreen

Function Main:Int()
	game = New ParticleTestApp
	Return 0
End

Class ParticleTestApp Extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		drawFPSOn = True
		testScreen = New ParticleTestScreen
		testScreen.PreStart()
		Return 0
	End
End

Class ParticleTestScreen Extends Screen
	Field ps:ParticleSystem
	Field pg:ParticleGroup
	Field e:Emitter
	Field e2:Emitter
	Field f:Force
	Field speck:Image

	Field emitting:Bool
	Field rendering:Bool = True

	Method New()
		name = "Particle System Test"
		speck = LoadImage("speck.png",,Image.MidHandle)
		CreateSystem()
	End

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

	Method Render:Void()
		Cls
		If rendering Then ps.Render()
		SetColor(255,255,255)
	End

	Method DebugRender:Void()
		SetColor(255,255,255)
		SetAlpha(1)
#If TARGET="android" Or TARGET="ios" Then
		DrawText("Tap top half to toggle emitter, bottom half to toggle rendering",0,30)
#Else
		DrawText("Space: toggle emitter, R: toggle rendering",0,30)
#End
		DrawText("pg.AliveParticles="+pg.AliveParticles,0,50)
	End

	Method Update:Void()
		If KeyHit(KEY_SPACE) Then emitting = Not emitting
		If KeyHit(KEY_R) Then rendering = Not rendering

		If TouchHit() Then
			If TouchY() < SCREEN_HEIGHT2 Then
				emitting = Not emitting
			Else
				rendering = Not rendering
			End
		End
		If emitting Then
			e.EmitAt(3, SCREEN_WIDTH2, SCREEN_HEIGHT2+50)
		End
		ps.Update(dt.frametime)
		If KeyHit(KEY_ESCAPE) Then
			game.screenFade.Start(50, True)
			game.nextScreen = game.exitScreen
		End
	End

	Method CreateSystem:Void()
		ps = New ParticleSystem()

		pg = New ParticleGroup(5000) ' group of 5000 particles
		ps.Groups.Add(pg)

		f = New ConstantForce(0,-150) ' constant downward force of 150 pixels per second per second
		pg.Forces.Add(f)

		e = New Emitter
		e.SetParticleRGBInterpolated(255,0,0,0,255,0) ' fade from red to green
		e.Life = 3 ' lives for 3 seconds
		e.SetPolarVelocity(PI/2, PI/3, 250, 50) ' points up with a spread of 60 degrees
		e.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e.ParticleImage = speck
		e.Group = pg

		e2 = New Emitter
		e2.Life = 2 ' lives for 2 seconds
		e2.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e2.SetPolarVelocity(0,2*PI,50,10) ' points right with a spread of 360 degrees (a full circle)
		e2.SetParticleRGBInterpolated(0,255,255,0,255,0) ' fade from cyan to green
		e.AddDeathEmitter(e2, 0.05) ' 5% chance to fire off this emitter when a particle from other emitter dies
	End
End


More storyboard stuff.

More storyboard stuff.

--HG--
branch : development

Strict

Private
Import mojo
Import functions
Import collections
Import xml

Public
Import exception

Const TRANSFORM_POSITION:Int = 0
Const TRANSFORM_ALPHA:Int = 1
Const TRANSFORM_SCALE:Int = 2
Const TRANSFORM_SCALE_VECTOR:Int = 3
Const TRANSFORM_ROTATION:Int = 4
Const TRANSFORM_COLOR:Int = 5
Const TRANSFORM_HANDLE:Int = 6
Const TRANSFORM_SHAKE:Int = 7
Const TRANSFORM_COUNT:Int = 8

Const EASE_NONE:Int = 0
Const EASE_IN_DOUBLE:Int = 1
Const EASE_IN:Int = 2
Const EASE_IN_HALF:Int = 3
Const EASE_OUT:Int = 4
Const EASE_OUT_HALF:Int = 5
Const EASE_OUT_DOUBLE:Int = 6
Const EASE_IN_OUT:Int = 7

Class Storyboard
Private
	Field sprites:ArrayList<StoryboardSprite> = New ArrayList<StoryboardSprite>
	Field sounds:ArrayList<StoryboardSound> = New ArrayList<StoryboardSound>

Public
	Function LoadXML:Storyboard(filename:String)
		Local parser:XMLParser = New XMLParser
		Local doc:XMLDocument = parser.ParseFile("storyboard.xml")
		Local root:XMLElement = doc.Root
		Local sb:Storyboard = New Storyboard
		For Local node:XMLElement = EachIn root.Children
			If node.Name = "layer" Then
				Local index:Int = Int(node.GetAttribute("index","0"))
				For Local spriteNode:XMLElement = EachIn node.Children
					If spriteNode.Name = "sprite" Then
						sb.sprites.Add(StoryboardSprite.CreateFromXML(spriteNode, index))
					End
				Next
			End
		Next
		sb.sprites.Sort()
		Return sb
	End

	#Rem
	Function LoadOSB:Storyboard(filename:String)
		Local osb:String = LoadString(filename)
		Local lines:List<String> = New List<String>
		Local s:Int = 0, e:Int = 0
		Local prev:Int = 0
		For Local e:Int = 0 Until osb.Length
			Local newline:Bool = False
			If osb[e] = 13 Then
				newline = True
			ElseIf osb[e] = 10 Then
				If prev <> 13 Then
					newline = True
				Else
					s += 1
				End
			End
			If newline Then
				lines.AddLast(osb[s..e])
				s = e+1
			End
			prev = osb[e]
		Next
		If s < e Then lines.AddLast(osb[s..e])
		For Local i:Int = 0 Until lines.Count
			Local str:String = lines.Get(i)
			If str.StartsWith("Sprite") Then
				Local tokens:String[] = str.Split(",")
				Local layerStr:String = tokens[1]
				Local imageName:String = tokens[3]
		Next
	End
	#End

	Method Update:Void(currentTime:Int)
		For Local i:Int = 0 Until sprites.Size
			Local sprite:StoryboardSprite = sprites.Get(i)
			sprite.Update(currentTime)
		Next
		'For Local i:Int = 0 Until sounds.Size
		'	Local sound:StoryboardSound = sounds.Get(i)
		'	sound.Update(currentTime)
		'Next
	End

	Method Render:Void()
		For Local i:Int = 0 Until sprites.Size
			Local sprite:StoryboardSprite = sprites.Get(i)
			sprite.Render()
		Next
	End
End

Class StoryboardElement Implements IComparable Abstract
Private
	Global nextId:Int = 0
	Field id:Int

Public
	Method New()
		id = nextId
		nextId += 1
	End

	Method Compare:Int(other:Object)
		Local o:StoryboardElement = StoryboardElement(other)
		If Not o Then Return -1
		If o = Self Then Return 0
		If id < o.id Then Return -1
		If id > o.id Then Return 1
		Return 0
	End

	Method Equals:Bool(other:Object)
		Return Compare(other)=0
	End
End

Class StoryboardSound Extends StoryboardElement
Private
	Field soundName:String
	Field sound:GameSound

Public
	Function CreateFromXML:StoryboardSound(node:XMLElement)
		Return Null
	End

	Method New(soundName:String)
		Super.New()
		Self.soundName = soundName
	End
End

Class StoryboardSprite Extends StoryboardElement
Private
	Field layer:Int
	Field imageName:String
	Field image:GameImage

	Field firstX:Float, firstY:Float
	field firstScaleX:Float, firstScaleY:Float
	Field firstScale:Float, firstRotation:Float
	Field firstRed:Float, firstGreen:Float, firstBlue:Float, firstAlpha:Float

	Field x:Float, y:Float
	field scaleX:Float, scaleY:Float
	Field scale:Float, rotation:Float
	Field red:Float, green:Float, blue:Float, alpha:Float

	Field transforms:ArrayList<StoryboardSpriteTransform> = New ArrayList<StoryboardSpriteTransform>
	Field currentTransforms:StoryboardSpriteTransform[] = New StoryboardSpriteTransform[TRANSFORM_COUNT]

Public
	Function CreateFromXML:StoryboardSprite(node:XMLElement, layer:Int)
		Local imageName:String = node.GetAttribute("image","")
		Local sprite:StoryboardSprite = New StoryboardSprite(imageName, layer)
		sprite.firstX = Float(node.GetAttribute("x","0"))
		sprite.firstY = Float(node.GetAttribute("y","0"))
		sprite.firstScaleX = Float(node.GetAttribute("scaleX","1"))
		sprite.firstScaleY = Float(node.GetAttribute("scaleY","1"))
		sprite.firstScale = Float(node.GetAttribute("scale","1"))
		sprite.firstRotation = Float(node.GetAttribute("rotation","0"))
		sprite.firstRed = Float(node.GetAttribute("red","255"))
		sprite.firstGreen = Float(node.GetAttribute("green","255"))
		sprite.firstBlue = Float(node.GetAttribute("blue","255"))
		sprite.firstAlpha = Float(node.GetAttribute("alpha","0"))
		CreateTransformsFromXML(sprite, node)
		Return sprite
	End

	Function CreateTransformsFromXML:Void(sprite:StoryboardSprite, node:XMLElement, timeOffset:Int=0)
		For Local childNode:XMLElement = EachIn node.Children
			Local name:String = childNode.Name
			If name = "group" Then
				Local loopCount:Int = Int(childNode.GetAttribute("loopCount","1"))
				Local startTime:Int = Int(childNode.GetAttribute("startTime","0"))
				Local endTime:Int = Int(childNode.GetAttribute("endTime","0"))
				Local myOffset:Int = timeOffset + startTime
				For Local i:Int = 0 Until loopCount
					CreateTransformsFromXML(sprite, childNode, myOffset)
					myOffset += endTime - startTime
				Next
			Else
				sprite.transforms.Add(StoryboardSpriteTransform.CreateFromXML(childNode, timeOffset))
			End
		Next
	End

	Method New(imageName:String, layer:Int)
		Super.New()
		Self.imageName = imageName
		Self.layer = layer
	End

	Method Compare:Int(other:Object)
		Local o:StoryboardSprite = StoryboardSprite(other)
		If Not o Then Return -1
		If o = Self Then Return 0
		If layer < o.layer Then Return -1
		If layer > o.layer Then Return 1
		Return Super.Compare(other)
	End

	Method Update:Void(currentTime:Int)
		x = firstX; y = firstY
		scaleX = firstScaleX; scaleY = firstScaleY
		scale = firstScale; rotation = firstRotation
		red = firstRed; green = firstGreen; blue = firstBlue; alpha = firstAlpha

		For Local i:Int = 0 Until transforms.Size
			transforms.Get(i).Update(currentTime)
		Next
		For Local i:Int = 0 Until currentTransforms.Length
			currentTransforms[i] = GetActiveTransform(i, currentTime)
			If currentTransforms[i] Then currentTransforms[i].Apply(Self)
		Next
	End

	Method Render:Void()
		If Not image Then image = game.images.Find(imageName)
		If Not image Then Return

		If alpha = 0 Then Return

		' translation, scale, rotation, handle, other effects
		PushMatrix
		Translate x, y
		Scale scaleX, scaleY
		Scale scale, scale
		Rotate rotation
		SetColor red, green, blue
		SetAlpha alpha

		image.Draw(0, 0)
		PopMatrix
	End

	Method GetActiveTransform:StoryboardSpriteTransform(findType:Int, atTime:Int)
		' loop on all children
		For Local i:Int = 0 Until transforms.Size
			Local tr:StoryboardSpriteTransform = transforms.Get(i)
			If tr.transformType = findType Then
				' if we're past the start time for this transform
				If atTime >= tr.startTime And atTime <= tr.endTime Then
					Return tr
				End
			End
		Next
		Return Null
	End
End

Class StoryboardSpriteTransform Implements IComparable
Private
	Global hslArray:Float[] = New Float[3]
	Global rgbArray:Int[] = New Int[3]

	Field transformType:Int = 0

	Field currentTime:Int
	Field startTime:Int
	Field endTime:Int
	Field easeType:Int

	Field startValues:Float[]
	Field endValues:Float[]
	Field currentValues:Float[]

Public
	Function CreateFromXML:StoryboardSpriteTransform(node:XMLElement, timeOffset:Int=0)
		Local name:String = node.Name
		Local startTime:Int = Int(node.GetAttribute("startTime","0"))+timeOffset
		Local endTime:Int = Int(node.GetAttribute("endTime","0"))+timeOffset
		Local easeType:Int = Int(node.GetAttribute("easeType","0"))
		If name = "scale" Then
			Local startScale:Float = Float(node.GetAttribute("startScale","1"))
			Local endScale:Float = Float(node.GetAttribute("endScale","1"))
			Return CreateScale(startTime, endTime, easeType, startScale, endScale)
		ElseIf name = "scaleVector" Then
			Local startScaleX:Float = Float(node.GetAttribute("startScaleX","1"))
			Local endScaleX:Float = Float(node.GetAttribute("endScaleX","1"))
			Local startScaleY:Float = Float(node.GetAttribute("startScaleY","1"))
			Local endScaleY:Float = Float(node.GetAttribute("endScaleY","1"))
			Return CreateScaleVector(startTime, endTime, easeType, startScaleX, startScaleY, endScaleX, endScaleY)
		ElseIf name = "alpha" Then
			Local startAlpha:Float = Float(node.GetAttribute("startAlpha","1"))
			Local endAlpha:Float = Float(node.GetAttribute("endAlpha","1"))
			Return CreateAlpha(startTime, endTime, easeType, startAlpha, endAlpha)
		ElseIf name = "rotation" Then
			Local startRotation:Float = Float(node.GetAttribute("startRotation","0"))
			Local endRotation:Float = Float(node.GetAttribute("endRotation","0"))
			Return CreateRotation(startTime, endTime, easeType, startRotation, endRotation)
		ElseIf name = "position" Then
			Local startX:Float = Float(node.GetAttribute("startX","0"))
			Local endX:Float = Float(node.GetAttribute("endX","0"))
			Local startY:Float = Float(node.GetAttribute("startY","0"))
			Local endY:Float = Float(node.GetAttribute("endY","0"))
			Return CreatePosition(startTime, endTime, easeType, startX, startY, endX, endY)
		ElseIf name = "handle" Then
			Local startX:Float = Float(node.GetAttribute("startX","0"))
			Local endX:Float = Float(node.GetAttribute("endX","0"))
			Local startY:Float = Float(node.GetAttribute("startY","0"))
			Local endY:Float = Float(node.GetAttribute("endY","0"))
			Return CreateHandle(startTime, endTime, easeType, startX, startY, endX, endY)
		ElseIf name = "color" Then
			If node.HasAttribute("startRed") Or node.HasAttribute("startGreen") Or node.HasAttribute("startBlue") Then
				Local startRed:Float = Float(node.GetAttribute("startRed","255"))
				Local endRed:Float = Float(node.GetAttribute("endRed","255"))
				Local startGreen:Float = Float(node.GetAttribute("startGreen","255"))
				Local endGreen:Float = Float(node.GetAttribute("endGreen","255"))
				Local startBlue:Float = Float(node.GetAttribute("startBlue","255"))
				Local endBlue:Float = Float(node.GetAttribute("endBlue","255"))
				Return CreateColorRGB(startTime, endTime, easeType, startRed, startGreen, startBlue, endRed, endGreen, endBlue)
			Else
				Local startHue:Float = Float(node.GetAttribute("startHue","0"))
				Local endHue:Float = Float(node.GetAttribute("endHue","0"))
				Local startSaturation:Float = Float(node.GetAttribute("startSaturation","1"))
				Local endSaturation:Float = Float(node.GetAttribute("endSaturation","1"))
				Local startLuminance:Float = Float(node.GetAttribute("startLuminance","1"))
				Local endLuminance:Float = Float(node.GetAttribute("endLuminance","1"))
				Return CreateColorHSL(startTime, endTime, easeType, startHue, startSaturation, startLuminance, endHue, endSaturation, endLuminance)
			End
		End
		Return Null
	End

	Function CreateScale:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startScale#, endScale#)
		Return New StoryboardSpriteTransform(TRANSFORM_SCALE, startTime, endTime, easeType, startScale, endScale)
	End

	Function CreateScaleVector:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startScaleX#, startScaleY#, endScaleX#, endScaleY#)
		Return New StoryboardSpriteTransform(TRANSFORM_SCALE_VECTOR, startTime, endTime, easeType, startScaleX, endScaleX, startScaleY, endScaleY)
	End

	Function CreateAlpha:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startAlpha#, endAlpha#)
		startAlpha = Max(0.0,Min(1.0,startAlpha))
		endAlpha = Max(0.0,Min(1.0,endAlpha))
		Return New StoryboardSpriteTransform(TRANSFORM_ALPHA, startTime, endTime, easeType, startAlpha, endAlpha)
	End

	Function CreateRotation:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startRotation#, endRotation#)
		Return New StoryboardSpriteTransform(TRANSFORM_ROTATION, startTime, endTime, easeType, startRotation, endRotation)
	End

	Function CreatePosition:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startX#, startY#, endX#, endY#)
		Return New StoryboardSpriteTransform(TRANSFORM_POSITION, startTime, endTime, easeType, startX, endX, startY, endY)
	End

	Function CreateHandle:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startX#, startY#, endX#, endY#)
		Return New StoryboardSpriteTransform(TRANSFORM_HANDLE, startTime, endTime, easeType, startX, endX, startY, endY)
	End

	Function CreateColorRGB:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startRed#, startGreen#, startBlue#, endRed#, endGreen#, endBlue#)
		startRed = Max(0.0,Min(255.0,startRed))
		startGreen = Max(0.0,Min(255.0,startGreen))
		startBlue = Max(0.0,Min(255.0,startBlue))
		endRed = Max(0.0,Min(255.0,endRed))
		endGreen = Max(0.0,Min(255.0,endGreen))
		endBlue = Max(0.0,Min(255.0,endBlue))
		RGBtoHSL(startRed, startGreen, startBlue, hslArray)
		Local startHue:Float = hslArray[0], startSaturation:Float = hslArray[1], startLuminance:Float = hslArray[2]
		RGBtoHSL(endRed, endGreen, endBlue, hslArray)
		Local endHue:Float = hslArray[0], endSaturation:Float = hslArray[1], endLuminance:Float = hslArray[2]
		Return CreateColorHSL(startTime, endTime, easeType, startHue, startSaturation, startLuminance, endHue, endSaturation, endLuminance)
	End

	Function CreateColorHSL:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startHue#, startSaturation#, startLuminance#, endHue#, endSaturation#, endLuminance#)
		startHue = Max(0.0, Min(1.0, startHue))
		startSaturation = Max(0.0, Min(1.0, startSaturation))
		startLuminance = Max(0.0, Min(1.0, startLuminance))
		endHue = Max(0.0, Min(1.0, endHue))
		endSaturation = Max(0.0, Min(1.0, endSaturation))
		endLuminance = Max(0.0, Min(1.0, endLuminance))
		Return New StoryboardSpriteTransform(TRANSFORM_COLOR, startTime, endTime, easeType, startHue, endHue, startSaturation, endSaturation, startLuminance, endLuminance)
	End

	Method New(transformType:Int, startTime:Int, endTime:Int, easeType:Int,
							startValue1:Float=0, endValue1:Float=0,
							startValue2:Float=0, endValue2:Float=0,
							startValue3:Float=0, endValue3:Float=0,
							startValue4:Float=0, endValue4:Float=0,
							startValue5:Float=0, endValue5:Float=0)
		Self.transformType = transformType
		Self.startTime = startTime
		Self.endTime = endTime
		Self.easeType = easeType
		Select transformType
			Case TRANSFORM_ALPHA, TRANSFORM_SCALE, TRANSFORM_ROTATION
				Init(1, startValue1, endValue1)
			Case TRANSFORM_POSITION, TRANSFORM_SCALE_VECTOR, TRANSFORM_HANDLE
				Init(2, startValue1, endValue1, startValue2, endValue2)
			Case TRANSFORM_COLOR
				Init(3, startValue1, endValue1, startValue2, endValue2, startValue3, endValue3)
		End
	End

	Method Update:Void(currentTime:Int)
		Self.currentTime = currentTime
		For Local i:Int = 0 Until startValues.Length
			currentValues[i] = Calculate(startValues[i], endValues[i])
		Next
	End

	Method Apply:Void(sprite:StoryboardSprite)
		Select transformType
			Case TRANSFORM_ALPHA
				sprite.alpha = currentValues[0]
			Case TRANSFORM_SCALE
				sprite.scale = currentValues[0]
			Case TRANSFORM_ROTATION
				sprite.rotation = currentValues[0]
			Case TRANSFORM_POSITION
				sprite.x = currentValues[0]
				sprite.y = currentValues[1]
			Case TRANSFORM_SCALE_VECTOR
				sprite.scaleX = currentValues[0]
				sprite.scaleY = currentValues[1]
			Case TRANSFORM_COLOR
				HSLtoRGB(currentValues[0], currentValues[1], currentValues[2], rgbArray)
				sprite.red = rgbArray[0]
				sprite.green = rgbArray[1]
				sprite.blue = rgbArray[2]
			Case TRANSFORM_HANDLE
				If sprite And sprite.image And sprite.image.image Then
					sprite.image.image.SetHandle(currentValues[0], currentValues[1])
				End
		End
	End

	Method Compare:Int(other:Object)
		Local o:StoryboardSpriteTransform = StoryboardSpriteTransform(other)
		If Not o Then Return -1
		If o = Self Then Return 0
		If startTime > o.startTime Then Return 1
		If startTime < o.startTime Then Return -1
		If endTime > o.endTime Then Return 1
		If endTime < o.endTime Then Return -1
		If transformType > o.transformType Then Return 1
		If transformType < o.transformType Then Return -1
		Return 0
	End

	Method Equals:Bool(other:Object)
		Return Compare(other)=0
	End

Private
	Method Init:Void(elementCount:Int,
							startValue1:Float=0, endValue1:Float=0,
							startValue2:Float=0, endValue2:Float=0,
							startValue3:Float=0, endValue3:Float=0,
							startValue4:Float=0, endValue4:Float=0,
							startValue5:Float=0, endValue5:Float=0)
		startValues = New Float[elementCount]
		endValues = New Float[elementCount]
		currentValues = New Float[elementCount]
		If elementCount >= 1 Then
			startValues[0] = startValue1
			endValues[0] = endValue1
		End
		If elementCount >= 2 Then
			startValues[1] = startValue2
			endValues[1] = endValue2
		End
		If elementCount >= 3 Then
			startValues[2] = startValue3
			endValues[2] = endValue3
		End
		If elementCount >= 4 Then
			startValues[3] = startValue4
			endValues[3] = endValue4
		End
		If elementCount >= 5 Then
			startValues[4] = startValue5
			endValues[4] = endValue5
		End
	End

	Method Calculate:Float(startValue:Float, endValue:Float)
		' shortcut for start/end (also accounts for the case when startTime==endTime)
		' we check endTime first because if startTime==endTime==currentTime, we want to use endValue
		If currentTime >= endTime Then Return endValue
		If currentTime <= startTime Then Return startValue

		' how far through are we?
		Local progress:Float = Float(currentTime - startTime) / Float(endTime - startTime)

		Select easeType
			Case EASE_IN_DOUBLE
				Return Lerp(endValue, startValue, (1-progress)*(1-progress)*(1-progress)*(1-progress))
			Case EASE_IN
				Return Lerp(endValue, startValue, (1-progress)*(1-progress))
			Case EASE_IN_HALF
				Return Lerp(endValue, startValue, Pow(1-progress, 1.5))
			Case EASE_OUT
				Return Lerp(startValue, endValue, progress * progress)
			Case EASE_OUT_HALF
				Return Lerp(startValue, endValue, Pow(progress, 1.5))
			Case EASE_OUT_DOUBLE
				Return Lerp(startValue, endValue, progress*progress*progress*progress)
			Case EASE_IN_OUT
				Return startValue + (-2*(progress*progress*progress) + 3*(progress*progress)) * (endValue - startValue)
			Default
				Return Lerp(startValue, endValue, progress);
		End
	End

	Method Lerp:Float(startValue:Float, endValue:Float, progress:Float)
		Return startValue + (endValue-startValue) * progress
	End
End

Strict

Public
Import exception

Private
Import mojo
Import functions
Import collections

Const TRANSFORM_POSITION:Int = 0
Const TRANSFORM_ALPHA:Int = 1
Const TRANSFORM_SCALE:Int = 2
Const TRANSFORM_SCALE_VECTOR:Int = 3
Const TRANSFORM_ROTATION:Int = 4
Const TRANSFORM_COLOR:Int = 5
Const TRANSFORM_HANDLE:Int = 6
Const TRANSFORM_SHAKE:Int = 7
Const TRANSFORM_COUNT:Int = 8

Const EASE_NONE:Int = 0
Const EASE_IN_DOUBLE:Int = 1
Const EASE_IN:Int = 2
Const EASE_IN_HALF:Int = 3
Const EASE_OUT:Int = 4
Const EASE_OUT_HALF:Int = 5
Const EASE_OUT_DOUBLE:Int = 6
Const EASE_IN_OUT:Int = 7

Class Storyboard
Private
	Field sprites:ArrayList<StoryboardSprite> = New ArrayList<StoryboardSprite>
	Field sounds:ArrayList<StoryboardSound> = New ArrayList<StoryboardSound>

Public
	Function LoadXML:Storyboard(filename:String)
		Local parser:XMLParser = New XMLParser
		Local doc:XMLDocument = parser.LoadFile("storyboard.xml")
		Local root:XMLElement = doc.Root
		Local sb:Storyboard = New Storyboard
		For Local node:XMLElement = EachIn root.Children
			If node.Name = "layer" Then
				Local index:Int = Int(node.GetAttribute("index","0"))
				For Local spriteNode:XMLElement = EachIn node.Children
					If spriteNode.Name = "sprite" Then
						sb.sprites.Add(StoryboardSprite.CreateFromXML(spriteNode, index))
					End
				Next
			End
		Next
		sb.sprites.Sort()
		Return sb
	End

	Method Update:Void(currentTime:Int)
		For Local i:Int = 0 Until sprites
			Local sprite:StoryboardSprite = sprites.Get(i)
			sprite.Update(currentTime)
		Next
		'For Local i:Int = 0 Until sounds
		'	Local sound:StoryboardSound = sounds.Get(i)
		'	sound.Update(currentTime)
		'Next
	End

	Method Render:Void()
		For Local i:Int = 0 Until sprites
			Local sprite:StoryboardSprite = sprites.Get(i)
			sprite.Render()
		Next
	End
End

Class StoryboardElement Implements IComparable Abstract
Private
	Global nextId:Int = 0
	Field id:Int

Public
	Method New()
		id = nextId
		nextId += 1
	End

	Method Compare:Int(other:Object)
		Local o:StoryboardElement = StoryboardElement(other)
		If Not o Then Return -1
		If o = Self Then Return 0
		If id < o.id Then Return -1
		If id > o.id Then Return 1
		Return 0
	End

	Method Equals:Bool(other:Object)
		Return Compare(other)=0
	End
End

Class StoryboardSound Extends StoryboardElement
Private
	Field soundName:String
	Field sound:GameSound

Public
	Function CreateFromXML:StoryboardSound(node:XMLElement)
		Return Null
	End

	Method New(soundName:String)
		Super.New()
		Self.soundName = soundName
	End
End

Class StoryboardSprite Extends StoryboardElement
Private
	Field layer:Int
	Field imageName:String
	Field image:GameImage

	Field transforms:ArrayList<StoryboardSpriteTransform> = New ArrayList<StoryboardSpriteTransform>
	Field currentTransforms:StoryboardSpriteTransform[] = New StoryboardSpriteTransform[TRANSFORM_COUNT]

Public
	Function CreateFromXML:StoryboardSprite(node:XMLElement, layer:Int)
		Local imageName:String = node.GetAttribute("imageName","")
		Local sprite:StoryboardSprite = New StoryboardSprite(imageName, layer)
		CreateTransformsFromXML(sprite, node)
		Return sprite
	End

	Function CreateTransformsFromXML:Void(sprite:StoryboardSprite, node:XMLElement, timeOffset:Int=0)
		For Local childNode:XMLElement = EachIn node.Children
			Local name:String = childNode.Name
			If name = "group" Then
				Local loopCount:Int = Int(childNode.GetAttribute("loopCount","1"))
				Local startTime:Int = Int(childNode.GetAttribute("startTime","0"))
				Local endTime:Int = Int(childNode.GetAttribute("endTime","0"))
				Local myOffset:Int = timeOffset + startTime
				For Local i:Int = 0 Until loopCount
					CreateTransformsFromXML(sprite, childNode, myOffset)
					myOffset += endTime - startTime
				Next
			Else
				sprite.transforms.Add(StoryboardSpriteTransform.CreateFromXML(childNode, timeOffset))
			End
		Next
	End

	Method New(imageName:String, layer:Int)
		Super.New()
		Self.imageName = imageName
		Self.layer = layer
	End

	Method Compare:Int(other:Object)
		Local o:StoryboardSprite = StoryboardSprite(other)
		If Not o Then Return -1
		If o = Self Then Return 0
		If layer < o.layer Then Return -1
		If layer > o.layer Then Return 1
		Return Super.Compare(other)
	End

	Method Update:Void(currentTime:Int)
		For Local i:Int = 0 Until transforms.Size
			transforms.Get(i).Update(currentTime)
		Next
		For Local i:Int = 0 Until currentTransforms.Length
			currentTransforms[i] = GetActiveTransform(i, currentTime)
		Next
	End

	Method Render:Void()
		If Not image Then image = game.images.Find(imageName)
		If Not image Then Return
		PushMatrix
		' translation, scale, rotation, handle, other effects
		If currentTransforms[TRANSFORM_POSITION] Then currentTransforms[TRANSFORM_POSITION].Apply()
		If currentTransforms[TRANSFORM_SCALE_VECTOR] Then currentTransforms[TRANSFORM_SCALE_VECTOR].Apply()
		If currentTransforms[TRANSFORM_SCALE] Then currentTransforms[TRANSFORM_SCALE].Apply()
		If currentTransforms[TRANSFORM_ROTATION] Then currentTransforms[TRANSFORM_ROTATION].Apply()
		If currentTransforms[TRANSFORM_HANDLE] Then currentTransforms[TRANSFORM_HANDLE].Apply()
		If currentTransforms[TRANSFORM_SHAKE] Then currentTransforms[TRANSFORM_SHAKE].Apply()
		If currentTransforms[TRANSFORM_COLOR] Then currentTransforms[TRANSFORM_COLOR].Apply()
		image.Draw(0, 0)
		PopMatrix
	End

	Method GetActiveTransform:StoryboardSpriteTransform(findType:Int, atTime:Int)
		' by default, we haven't found anything
		Local active:StoryboardSpriteTransform = Null
		' loop on all children
		For Local i:Int = 0 Until transforms.Size
			Local tr:StoryboardSpriteTransform = transforms.Get(i)
			If tr.transformType = findType Then
				' if we're past the start time for this transform
				If atTime >= tr.startTime Then
					active = child
				Else
					' we've gone past the current time, so break out
					Exit
				End
			End
		Next
		' return what we have
		Return active
	End
End

Class StoryboardSpriteTransform Implements IComparable
Private
	Global hslArray:Float[] = New Float[3]
	Global rgbArray:Int[] = New Int[3]

	Field transformType:Int = TRANSFORM_NONE

	Field currentTime:Int
	Field startTime:Int
	Field endTime:Int
	Field easeType:Int

	Field startValues:Float[]
	Field endValues:Float[]
	Field currentValues:Float[]

Public
	Function CreateFromXML:StoryboardSpriteTransform(node:XMLElement, timeOffset:Int=0)
		Local name:String = node.Name
		Local startTime:Int = Int(node.GetAttribute("startTime","0"))+timeOffset
		Local endTime:Int = Int(node.GetAttribute("endTime","0"))+timeOffset
		Local easeType:Int = Int(node.GetAttribute("easeType","0"))
		If name = "scale" Then
			Local startScale:Float = Float(node.GetAttribute("startScale","1"))
			Local endScale:Float = Float(node.GetAttribute("endScale","1"))
			Return CreateScale(startTime, endTime, easeType, startScale, endScale)
		ElseIf name = "scaleVector" Then
			Local startScaleX:Float = Float(node.GetAttribute("startScaleX","1"))
			Local endScaleX:Float = Float(node.GetAttribute("endScaleX","1"))
			Local startScaleY:Float = Float(node.GetAttribute("startScaleY","1"))
			Local endScaleY:Float = Float(node.GetAttribute("endScaleY","1"))
			Return CreateScaleVector(startTime, endTime, easeType, startScaleX, startScaleY, endScaleX, endScaleY)
		ElseIf name = "alpha" Then
			Local startAlpha:Float = Float(node.GetAttribute("startAlpha","1"))
			Local endAlpha:Float = Float(node.GetAttribute("endAlpha","1"))
			Return CreateAlpha(startTime, endTime, easeType, startAlpha, endAlpha)
		ElseIf name = "rotation" Then
			Local startRotation:Float = Float(node.GetAttribute("startRotation","0"))
			Local endRotation:Float = Float(node.GetAttribute("endRotation","0"))
			Return CreateRotation(startTime, endTime, easeType, startRotation, endRotation)
		ElseIf name = "position" Then
			Local startX:Float = Float(node.GetAttribute("startX","0"))
			Local endX:Float = Float(node.GetAttribute("endX","0"))
			Local startY:Float = Float(node.GetAttribute("startY","0"))
			Local endY:Float = Float(node.GetAttribute("endY","0"))
			Return CreatePosition(startTime, endTime, easeType, startX, startY, endX, endY)
		ElseIf name = "handle" Then
			Local startX:Float = Float(node.GetAttribute("startX","0"))
			Local endX:Float = Float(node.GetAttribute("endX","0"))
			Local startY:Float = Float(node.GetAttribute("startY","0"))
			Local endY:Float = Float(node.GetAttribute("endY","0"))
			Return CreateHandle(startTime, endTime, easeType, startX, startY, endX, endY)
		ElseIf name = "color" Then
			If node.HasAttribute("startRed") Or node.HasAttribute("startGreen") Or node.HasAttribute("startBlue") Then
				Local startRed:Float = Float(node.GetAttribute("startRed","255"))
				Local endRed:Float = Float(node.GetAttribute("endRed","255"))
				Local startGreen:Float = Float(node.GetAttribute("startGreen","255"))
				Local endGreen:Float = Float(node.GetAttribute("endGreen","255"))
				Local startBlue:Float = Float(node.GetAttribute("startBlue","255"))
				Local endBlue:Float = Float(node.GetAttribute("endBlue","255"))
				Return CreateColorRGB(startTime, endTime, easeType, startRed, startGreen, startBlue, endRed, endGreen, endBlue)
			Else
				Local startHue:Float = Float(node.GetAttribute("startHue","0"))
				Local endHue:Float = Float(node.GetAttribute("endHue","0"))
				Local startSaturation:Float = Float(node.GetAttribute("startSaturation","1"))
				Local endSaturation:Float = Float(node.GetAttribute("endSaturation","1"))
				Local startLuminance:Float = Float(node.GetAttribute("startLuminance","1"))
				Local endLuminance:Float = Float(node.GetAttribute("endLuminance","1"))
				Return CreateColorHSL(startTime, endTime, easeType, startHue, startSaturation, startLuminance, endHue, endSaturation, endLuminance)
			End
		End
	End

	Function CreateScale:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startScale#, endScale#)
		Return New StoryboardSpriteTransform(TRANSFORM_SCALE, startTime, endTime, easeType, startScale, endScale)
	End

	Function CreateScaleVector:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startScaleX#, startScaleY#, endScaleX#, endScaleY#)
		Return New StoryboardSpriteTransform(TRANSFORM_SCALE_VECTOR, startTime, endTime, easeType, startScaleX, endScaleX, startScaleY, endScaleY)
	End

	Function CreateAlpha:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startAlpha#, endAlpha#)
		startAlpha = Max(0.0,Min(1.0,startAlpha))
		endAlpha = Max(0.0,Min(1.0,endAlpha))
		Return New StoryboardSpriteTransform(TRANSFORM_ALPHA, startTime, endTime, easeType, startAlpha, endAlpha)
	End

	Function CreateRotation:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startRotation#, endRotation#)
		Return New StoryboardSpriteTransform(TRANSFORM_ROTATION, startTime, endTime, easeType, startRotation, endRotation)
	End

	Function CreatePosition:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startX#, startY#, endX#, endY#)
		Return New StoryboardSpriteTransform(TRANSFORM_POSITION, startTime, endTime, easeType, startX, endX, startY, endY)
	End

	Function CreateHandle:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startX#, startY#, endX#, endY#)
		Return New StoryboardSpriteTransform(TRANSFORM_HANDLE, startTime, endTime, easeType, startX, endX, startY, endY)
	End

	Function CreateColorRGB:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startRed#, startGreen#, startBlue#, endRed#, endGreen#, endBlue#)
		startRed = Max(0.0,Min(255.0,startRed))
		startGreen = Max(0.0,Min(255.0,startGreen))
		startBlue = Max(0.0,Min(255.0,startBlue))
		endRed = Max(0.0,Min(255.0,endRed))
		endGreen = Max(0.0,Min(255.0,endGreen))
		endBlue = Max(0.0,Min(255.0,endBlue))
		RGBtoHSL(startRed, startGreen, startBlue, hslArray)
		Local startHue:Float = hslArray[0], startSaturation:Float = hslArray[1], startLuminance:Float = hslArray[2]
		RGBtoHSL(endRed, endGreen, endBlue, hslArray)
		Local endHue:Float = hslArray[0], endSaturation:Float = hslArray[1], endLuminance:Float = hslArray[2]
		Return CreateColorHSL(startTime, endTime, easeType, startHue, startSaturation, startLuminance, endHue, endSaturation, endLuminance)
	End

	Function CreateColorHSL:StoryboardSpriteTransform(startTime%, endTime%, easeType%, startHue#, startSaturation#, startLuminance#, endHue#, endSaturation#, endLuminance#)
		startHue = Max(0.0,Min(1.0,startHue))
		startSaturation = Max(0.0,Min(1.0,startGreen))
		startLuminance = Max(0.0,Min(1.0,startBlue))
		endHue = Max(0.0,Min(1.0,endHue))
		endSaturation = Max(0.0,Min(1.0,endGreen))
		endLuminance = Max(0.0,Min(1.0,endBlue))
		Return New StoryboardSpriteTransform(TRANSFORM_COLOR, startTime, endTime, easeType, startHue, endHue, startSaturation, endSaturation, startLuminance, endLuminance)
	End

	Method New(transformType:Int, startTime:Int, endTime:Int, easeType:Int
							startValue1:Float=0, endValue1:Float=0,
							startValue2:Float=0, endValue2:Float=0,
							startValue3:Float=0, endValue3:Float=0,
							startValue4:Float=0, endValue4:Float=0,
							startValue5:Float=0, endValue5:Float=0)
		Self.transformType = transformType
		Self.startTime = startTime
		Self.endTime = endTime
		Self.easeType = easeType
		Select transformType
			Case TRANSFORM_ALPHA, Case TRANSFORM_SCALE, Case TRANSFORM_ROTATION
				Init(1, startValue1, endValue1)
			Case TRANSFORM_POSITION, TRANSFORM_SCALE_VECTOR, TRANSFORM_HANDLE
				Init(2, startValue1, endValue1, startValue2, endValue2)
			Case TRANSFORM_COLOR
				Init(3, startValue1, endValue1, startValue2, endValue2, startValue3, endValue3)
		End
	End

	Method Update:Void(currentTime:Int)
		Self.currentTime = currentTime
		For Local i:Int = 0 Until startValues.Length
			currentValues[i] = Calculate(startValues[i], endValues[i])
		Next
	End

	Method Apply:Void(sprite:StoryboardSprite=Null)
		Select type
			Case TRANSFORM_ALPHA
				SetAlpha(currentValues[0])
			Case TRANSFORM_SCALE
				Scale(currentValues[0], currentValues[0])
			Case TRANSFORM_ROTATION
				Rotate(currentValues[0])
			Case TRANSFORM_POSITION, TRANSFORM_HANDLE
				Translate(currentValues[0], currentValues[1])
			Case TRANSFORM_SCALE_VECTOR
				Scale(currentValues[0], currentValues[1])
			Case TRANSFORM_COLOR
				HSLtoRGB(currentValues[0], currentValues[1], currentValues[2], rgbArray)
				SetColor(rgbArray[0], rgbArray[1], rgbArray[2])
			Case TRANSFORM_HANDLE
				If sprite And sprite.image And sprite.image.image Then
					sprite.image.image.SetHandle(currentValues[0], currentValues[1])
				End
		End
	End

	Method Compare:Int(other:Object)
		Local o:StoryboardSpriteTransform = StoryboardSpriteTransform(other)
		If Not o Then Return -1
		If o = Self Then Return 0
		If startTime > o.startTime Then Return 1
		If startTime < o.startTime Then Return -1
		If endTime > o.endTime Then Return 1
		If endTime < o.endTime Then Return -1
		If transformType > o.transformType Then Return 1
		If transformType < o.transformType Then Return -1
		Return 0
	End

	Method Equals:Bool(other:Object)
		Return Compare(other)=0
	End

Private
	Method Init:Void(elementCount:Int,
							startValue1:Float=0, endValue1:Float=0,
							startValue2:Float=0, endValue2:Float=0,
							startValue3:Float=0, endValue3:Float=0,
							startValue4:Float=0, endValue4:Float=0,
							startValue5:Float=0, endValue5:Float=0)
		startValues = New Float[elementCount]
		endValues = New Float[elementCount]
		currentValues = New Float[elementCount]
		If elementCount <= 1 Then
			startValues[0] = startValue1
			endValues[0] = endValue1
		End
		If elementCount <= 2 Then
			startValues[1] = startValue2
			endValues[1] = endValue2
		End
		If elementCount <= 3 Then
			startValues[2] = startValue3
			endValues[2] = endValue3
		End
		If elementCount <= 4 Then
			startValues[3] = startValue4
			endValues[3] = endValue4
		End
		If elementCount <= 5 Then
			startValues[4] = startValue5
			endValues[4] = endValue5
		End
	End

	Method Calculate:Float(startValue:Float, endValue:Float)
		' shortcut for start/end (also accounts for the case when startTime==endTime)
		' we check endTime first because if startTime==endTime==currentTime, we want to use endValue
		If currentTime >= endTime Then Return endValue
		If currentTime <= startTime Then Return startValue

		' how far through are we?
		Local progress:Float = Float(currentTime - startTime) / Float(endTime - startTime)

		Select easeType
			Case EASE_IN_DOUBLE
				Return Lerp(endValue, startValue, (1-progress)*(1-progress)*(1-progress)*(1-progress))
			Case EASE_IN
				Return Lerp(endValue, startValue, (1-progress)*(1-progress))
			Case EASE_IN_HALF
				Return Lerp(endValue, startValue, Pow(1-progress, 1.5))
			Case EASE_OUT
				Return Lerp(startValue, endValue, progress * progress)
			Case EASE_OUT_HALF
				Return Lerp(startValue, endValue, Pow(progress, 1.5))
			Case EASE_OUT_DOUBLE
				Return Lerp(startValue, endValue, progress*progress*progress*progress)
			Case EASE_IN_OUT
				Return startValue + (-2*(progress*progress*progress) + 3*(progress*progress)) * (endValue - startValue)
			Default
				Return Lerp(startValue, endValue, progress);
		End
	End

	Method Lerp:Float(startValue:Float, endValue:Float, progress:Float)
		Return startValue + (endValue-startValue) * progress
	End
End


Testing new functionality.

Testing new functionality.

--HG--
extra : convert_revision : svn%3Ae934d3cd-0d57-f34a-94d0-9e8d31ae1c29/trunk%40204

Strict

Import mojo
Import diddy

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

Global guiScreen:GUIScreen
Global xmlParser:XMLParser = New XMLParser
Global guiSkin:XMLDocument

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()

		game.images.Load("continue.png",,False)
		game.images.Load("continueMO.png",,False)
		game.images.Load("newgame.png",,False)
		game.images.Load("newgameMO.png",,False)
		game.images.Load("options.png",,False)
		game.images.Load("optionsMO.png",,False)

		' enable this and UseVirtualResolution to test the GUI under virtual res
		'SetScreenSize(1024, 768)

		Font.LoadFonts(xmlParser.ParseString(LoadString("fonts.xml")))
		guiSkin = xmlParser.ParseString(LoadString("defaultguiskin.xml"))

		drawFPSOn = True

		guiScreen = new GUIScreen
		guiScreen.PreStart()
		return 0
	End
End

Class GUIScreen Extends Screen
	Field mygui:MyGUI = New MyGUI

	Method New()
		name = "Game"
	End

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

	Method Render:Void()
		Cls
		mygui.Draw()
		SetColor(255, 255, 255)
		DrawText("slider value="+mygui.slider.Value, 0, 20)
		DrawText("radio value="+mygui.rg.SelectedValue, 0, 40)
	End

	Method Update:Void()
		mygui.Update()
		If KeyHit(KEY_ESCAPE)
			game.screenFade.Start(50, true)
			game.nextScreen = game.exitScreen
		End
	End
End

Class MyGUI Extends GUI
	Field window1:Window
	Field button1:Button
	Field button2:Button
	Field button3:Button
	Field checkbox:Checkbox

	Field window2:Window
	Field rg:RadioGroup
	Field slider:Slider
	Field radio1:RadioButton
	Field radio2:RadioButton
	Field radio3:RadioButton
	Field radio4:RadioButton
	Field radio5:RadioButton

	Field window3:Window
	Field imageButton1:Button
	Field imageButton2:Button
	Field imageButton3:Button

	Field testPanel:Panel

	Method New()
		' by default the GUI will always ignore virtual resolution and display using 1:1 with the device resolution
		'UseVirtualResolution = True
		Self.LayoutEnabled = True
		' Note: LoadSkin (should) work at any point of the GUI lifecycle
		LoadSkin(guiSkin)

		window1 = New Window(Self.Desktop)
		window1.ShowMaximize = True
		window1.SetBounds(50,70,200,200)
		window1.Title = "GridLayout Demo"
		window1.ContentPane.LayoutManager = New GridLayout(0,2)

		button1 = New Button(window1.ContentPane)
		button1.Text = "Button 1"
		button1.LayoutData = New GridData(1,2)
		button2 = New Button(window1.ContentPane)
		button2.Text = "Button 2"
		button3 = New Button(window1.ContentPane)
		button3.Text = "Button 3"

		window2 = New Window(Self.Desktop)
		window2.ShowMinimize = True
		window2.Title = "Form Components Example"
		window2.SetBounds(300,200,230,200)

		radio1 = New RadioButton(window2.ContentPane)
		radio1.Text = "Lorum"
		radio1.SetBounds(10,10,100,15)

		radio2 = New RadioButton(window2.ContentPane)
		radio2.Text = "Ipsum"
		radio2.SetBounds(10,30,100,15)

		radio3 = New RadioButton(window2.ContentPane)
		radio3.Text = "Dolor"
		radio3.SetBounds(10,50,100,15)

		radio4 = New RadioButton(window2.ContentPane)
		radio4.Text = "Sit"
		radio4.SetBounds(10,70,100,15)

		radio5 = New RadioButton(window2.ContentPane)
		radio5.Text = "Amet"
		radio5.SetBounds(10,90,100,15)

		rg = New RadioGroup
		rg.AddButton(radio1, "L")
		rg.AddButton(radio2, "I")
		rg.AddButton(radio3, "D")
		rg.AddButton(radio4, "S")
		rg.AddButton(radio5, "A")
		rg.SelectedValue = "I"

		slider = New Slider(window2.ContentPane)
		slider.SetBounds(10,110,200,15)
		slider.ShowButtons = True

		checkbox = New Checkbox(window2.ContentPane)
		checkbox.Text = "Does diddy rock?"
		checkbox.SetBounds(10,130,150,15)

		'window3 = New Window(Self.Desktop)
		'window3.ShowShade = True
		'window3.ShowClose = False
		'window3.Title = "Image Button Demo"
		'window3.SetBounds(350,50,260,200)

		'window3.ContentPane.LayoutManager = New GridLayout(0,1)'FlowLayout(FlowLayout.FLOW_VERTICAL)
		' test panel not using a window
		testPanel = New Panel(Self.Desktop)
		Local gl:GridLayout = New GridLayout(0,1)
		testPanel.LayoutManager = gl
		gl.rowHeightTypes[0] = GridLayout.FILLTYPE_PREFERRED
		gl.rowHeightTypes[1] = GridLayout.FILLTYPE_PREFERRED
		gl.rowHeightTypes[2] = GridLayout.FILLTYPE_PREFERRED
		gl.colWidthTypes[0] = GridLayout.FILLTYPE_PREFERRED

		imageButton1 = New Button(testPanel, game.images.Find("continue"), game.images.Find("continueMO"))
		imageButton2 = New Button(testPanel, game.images.Find("newgame"), game.images.Find("newgameMO"))
		imageButton3 = New Button(testPanel, game.images.Find("options"), game.images.Find("optionsMO"))

		testPanel.Pack()
		testPanel.SetLocation((Self.Desktop.Width-testPanel.Width)/2,Self.Desktop.Height-testPanel.Height)

		' Layout manager is disabled by default (to stop nasty recursion spam while building up the gui); this line enables it.
		' From here on, any changes to the gui structure that affect the layout will cause it to fire.
		Self.LayoutEnabled = True
	End

	Method ActionPerformed:Void(source:Component, action:String)
		If source = slider And action = ACTION_VALUE_CHANGED Then
			Print "slider="+slider.Value
		ElseIf source = button1 And action = ACTION_CLICKED Then
			Print("pressed button1!")
			slider.Value = 10
		ElseIf source = button2 And action = ACTION_CLICKED Then
			Print("pressed button2!")
			slider.Value = 20
		ElseIf source = button3 And action = ACTION_CLICKED Then
			Print("pressed button3!")
			slider.Value = 30
		ElseIf source = imageButton1 And action = ACTION_CLICKED Then
			Print("Pressed continue!")
			slider.Value = 40
		ElseIf source = imageButton2 And action = ACTION_CLICKED Then
			Print("Pressed new game!")
			slider.Value = 50
		ElseIf source = imageButton3 And action = ACTION_CLICKED Then
			Print("Pressed options!")
			slider.Value = 60
		ElseIf source = checkbox And action = ACTION_CLICKED Then
			If checkbox.Selected Then
				Print("pressed checkbox! selected=true")
			Else
				Print("pressed checkbox! selected=false")
			End
		ElseIf RadioButton(source) <> Null Then
			Print("pressed radiobutton! value="+rg.SelectedValue)
		End
	End
End

Strict

Import mojo
Import diddy

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

Global guiScreen:GUIScreen

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()

		game.images.Load("continue.png",,False)
		game.images.Load("continueMO.png",,False)
		game.images.Load("newgame.png",,False)
		game.images.Load("newgameMO.png",,False)
		game.images.Load("options.png",,False)
		game.images.Load("optionsMO.png",,False)

		' enable this and UseVirtualResolution to test the GUI under virtual res
		'SetScreenSize(1024, 768)

		Local parser:XMLParser = New XMLParser
		Font.LoadFonts(parser.ParseString(LoadString("fonts.xml")))
		drawFPSOn = True

		guiScreen = new GUIScreen
		guiScreen.PreStart()
		return 0
	End
End

Class GUIScreen Extends Screen
	Field mygui:MyGUI = New MyGUI

	Method New()
		name = "Game"
	End

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

	Method Render:Void()
		Cls
		mygui.Draw()
		SetColor(255, 255, 255)
		DrawText("slider value="+mygui.slider.Value, 0, 20)
		DrawText("radio value="+mygui.rg.SelectedValue, 0, 40)
	End

	Method Update:Void()
		mygui.Update()
		If KeyHit(KEY_ESCAPE)
			game.screenFade.Start(50, true)
			game.nextScreen = game.exitScreen
		End
	End
End

Class MyGUI Extends GUI
	Field window1:Window
	Field button1:Button
	Field button2:Button
	Field button3:Button
	Field checkbox:Checkbox

	Field window2:Window
	Field rg:RadioGroup
	Field slider:Slider
	Field radio1:RadioButton
	Field radio2:RadioButton
	Field radio3:RadioButton
	Field radio4:RadioButton
	Field radio5:RadioButton

	Field window3:Window
	Field imageButton1:Button
	Field imageButton2:Button
	Field imageButton3:Button

	Method New()
		' by default the GUI will always ignore virtual resolution and display using 1:1 with the device resolution
		'UseVirtualResolution = True

		Local parser:XMLParser = New XMLParser
		Local guiskin:XMLDocument = parser.ParseString(LoadString("defaultguiskin.xml"))

		' Note: LoadSkin (should) work at any point of the GUI lifecycle
		LoadSkin(guiskin)

		window1 = New Window(Desktop)
		window1.SetBounds(50,70,200,200)
		window1.Title = "GridLayout Demo"
		window1.ContentPane.LayoutManager = New GridLayout(0,2)

		button1 = New Button(window1.ContentPane)
		button1.Text = "Button 1"
		button1.LayoutData = New GridData(1,2)
		button2 = New Button(window1.ContentPane)
		button2.Text = "Button 2"
		button3 = New Button(window1.ContentPane)
		button3.Text = "Button 3"

		window2 = New Window(Desktop)
		window2.Title = "Form Components Example"
		window2.SetBounds(300,200,230,200)

		radio1 = New RadioButton(window2.ContentPane)
		radio1.Text = "Lorum"
		radio1.SetBounds(10,10,100,15)

		radio2 = New RadioButton(window2.ContentPane)
		radio2.Text = "Ipsum"
		radio2.SetBounds(10,30,100,15)

		radio3 = New RadioButton(window2.ContentPane)
		radio3.Text = "Dolor"
		radio3.SetBounds(10,50,100,15)

		radio4 = New RadioButton(window2.ContentPane)
		radio4.Text = "Sit"
		radio4.SetBounds(10,70,100,15)

		radio5 = New RadioButton(window2.ContentPane)
		radio5.Text = "Amet"
		radio5.SetBounds(10,90,100,15)

		rg = New RadioGroup
		rg.AddButton(radio1, "L")
		rg.AddButton(radio2, "I")
		rg.AddButton(radio3, "D")
		rg.AddButton(radio4, "S")
		rg.AddButton(radio5, "A")
		rg.SelectedValue = "I"

		slider = New Slider(window2.ContentPane)
		slider.SetBounds(10,110,200,15)
		slider.ShowButtons = True

		checkbox = New Checkbox(window2.ContentPane)
		checkbox.Text = "Does diddy rock?"
		checkbox.SetBounds(10,130,150,15)

		window3 = New Window(Desktop)
		window3.Title = "Image Button Demo"
		window3.SetBounds(350,50,260,200)

		imageButton1 = New Button(window3.ContentPane, game.images.Find("continue"), game.images.Find("continueMO"))
		imageButton1.SetLocation(20,20)

		imageButton2 = New Button(window3.ContentPane, game.images.Find("newgame"), game.images.Find("newgameMO"))
		imageButton2.SetLocation(20,imageButton1.Y+imageButton1.Height)

		imageButton3 = New Button(window3.ContentPane, game.images.Find("options"), game.images.Find("optionsMO"))
		imageButton3.SetLocation(20,imageButton2.Y+imageButton2.Height)
	End

	Method ActionPerformed:Void(source:Component, action:String)
		If source = slider And action = ACTION_VALUE_CHANGED Then
			Print "slider="+slider.Value
		ElseIf source = button1 And action = ACTION_CLICKED Then
			Print("pressed button1!")
		ElseIf source = button2 And action = ACTION_CLICKED Then
			Print("pressed button2!")
		ElseIf source = button3 And action = ACTION_CLICKED Then
			Print("pressed button3!")
		ElseIf source = imageButton1 And action = ACTION_CLICKED Then
			Print("Pressed continue!")
		ElseIf source = imageButton2 And action = ACTION_CLICKED Then
			Print("Pressed new game!")
		ElseIf source = imageButton3 And action = ACTION_CLICKED Then
			Print("Pressed options!")
		ElseIf source = checkbox And action = ACTION_CLICKED Then
			If checkbox.Selected Then
				Print("pressed checkbox! selected=true")
			Else
				Print("pressed checkbox! selected=false")
			End
		ElseIf RadioButton(source) <> Null Then
			Print("pressed radiobutton! value="+rg.SelectedValue)
		End
	End
End