Added click sound field

Added click sound field

Strict

Import mojo
Import functions

'Device width and height
Global DEVICE_WIDTH%
Global DEVICE_HEIGHT%

' Screen width and height
Global SCREEN_WIDTH#
Global SCREEN_HEIGHT#

' Half of SCREEN_WIDTH and HEIGHT
Global SCREEN_WIDTH2#
Global SCREEN_HEIGHT2#

' Used for Virtual Res
Global SCREENX_RATIO# = 1
Global SCREENY_RATIO# = 1

' THE GAME!!
Global game:DiddyApp

' Used for delta timing movement
Global dt:DeltaTimer

Class DiddyApp Extends App

	Field debugOn:Bool = False
	Field drawFPSOn:Bool = False

	Field virtualResOn:Bool = True

	Field FPS% = 60

	' current Screen
	Field currentScreen:Screen
	' next Screen
	Field nextScreen:Screen
	' exit Screen
	Field exitScreen:ExitScreen = new ExitScreen()
	' used for fading
	Field screenFade:ScreenFade = New ScreenFade

	' mouse
	Field mouseX:Int, mouseY:Int
	Field mouseHit:Int

	' Store the images here
	Global images:ImageBank = New ImageBank
	' Store the sounds here
	Global sounds:SoundBank = New SoundBank
	' volume control
	Field soundVolume:Int = 100
	Field musicVolume:Int = 100
	Field musicOkay:Int

	Field clickSound:GameSound

	Method OnCreate:Int()
		' Store the device width and height
		DEVICE_WIDTH = DeviceWidth()
		DEVICE_HEIGHT = DeviceHeight()

		SetScreenSize(DEVICE_WIDTH, DEVICE_HEIGHT)

		' set the mouse x,y
		mouseX = MouseX() / SCREENX_RATIO
		mouseY = MouseY() / SCREENY_RATIO

		' Set the Random seed
		Seed = RealMillisecs()
		' Create the delta timer
		dt = New DeltaTimer(FPS)
		SetUpdateRate FPS

		'create all the particles
		Particle.Cache()

		Return 0
	End

	Method SetScreenSize:Void(w:Float, h:Float)
		SCREEN_WIDTH = w
		SCREEN_HEIGHT = h
		SCREEN_WIDTH2 = SCREEN_WIDTH / 2
		SCREEN_HEIGHT2 = SCREEN_HEIGHT / 2

		SCREENX_RATIO = DEVICE_WIDTH/SCREEN_WIDTH
		SCREENY_RATIO = DEVICE_HEIGHT/SCREEN_HEIGHT

		If SCREENX_RATIO <> 1 Or SCREENY_RATIO <> 1
			virtualResOn = True
		End
	End

	Method OnRender:Int()
		FPSCounter.Update()
		If virtualResOn
			PushMatrix
			Scale SCREENX_RATIO, SCREENY_RATIO
		End
			currentScreen.Render()
			If screenFade.active then screenFade.Render()
		if virtualResOn
			PopMatrix
		End
		currentScreen.ExtraRender()
		If debugOn
			DrawDebug()
		End
		If drawFPSOn
			DrawFPS()
		End
		Return 0
	End

	Method OnUpdate:Int()
		dt.UpdateDelta()
		ScreenLogic()

		Return 0
	End

	Method ScreenLogic:Int()
		mouseX = MouseX() / SCREENX_RATIO
		mouseY = MouseY() / SCREENY_RATIO
		mouseHit = MouseHit()

		If screenFade.active then screenFade.Update()
		currentScreen.Update()
	End

	Method DrawDebug:Void()
		SetColor 255, 255, 255
		FPSCounter.Draw(0,0)
		DrawText "Screen         = "+currentScreen.name, 0, 10
		DrawText "Delta          = "+FormatNumber(dt.delta, 2) , 0, 20
		DrawText "Screen Width   = "+SCREEN_WIDTH, 0, 30
		DrawText "Screen Height  = "+SCREEN_HEIGHT, 0, 40
		DrawText "VMouseX        = "+Self.mouseX, 0, 50
		DrawText "VMouseY        = "+Self.mouseY, 0, 60
		DrawText "MouseX         = "+MouseX(), 0, 70
		DrawText "MouseY         = "+MouseY(), 0, 80
		DrawText "MusicOkay      = "+musicOkay, 0, 90
		DrawText "Music State    = "+MusicState(), 0, 100
		DrawText "Music Volume   = "+Self.musicVolume, 0, 110
		DrawText "Sound Volume   = "+Self.soundVolume, 0, 120
		DrawText "Sound Channel  = "+SoundPlayer.channel, 0, 130
	End

	Method DrawFPS:Void()
		DrawText FPSCounter.totalFPS, 0, 0
	End

	Method MusicPlay:Void(file:String, flags:Int=1)
		SetMusicVolume(musicVolume/100.0)
		musicOkay = PlayMusic("music/"+file, flags)
	End

	Method MusicSetVolume:Void(volume:Int)
		If volume < 0 Then volume = 0
		If volume > 100 Then volume = 100
		Self.musicVolume = volume
		SetMusicVolume(musicVolume/100.0)
	End

	Method SoundSetVolume:Void(volume:Int)
		If volume < 0 Then volume = 0
		If volume > 100 Then volume = 100
		Self.soundVolume = volume
		For Local i% = 0 To SoundPlayer.MAX_CHANNELS
			SetChannelVolume(i, game.soundVolume / 100.0)
		Next
	End

End

Class ScreenFade
	Field fadeTime:Float
	Field fadeOut:Bool
	Field ratio:Float = 0
	Field active:Bool
	Field counter:Float
	Field fadeMusic:Bool
	Field fadeSound:Bool

	Method Start:Void(fadeTime:Float, fadeOut:Bool, fadeSound:Bool = False, fadeMusic:Bool = False)
		If active Then Return
		active = True
		Self.fadeTime = fadeTime
		Self.fadeOut = fadeOut
		Self.fadeMusic = fadeMusic
		Self.fadeSound = fadeSound
		If fadeOut Then
			ratio = 1
		Else
			ratio = 0
		End
		counter = 0
	End

	Method Update:Void()
		If Not active Return
		counter += dt.delta
		CalcRatio()
		If fadeSound Then
			For Local i% = 0 To SoundPlayer.MAX_CHANNELS
				SetChannelVolume(i, (ratio) * (game.soundVolume / 100.0))
			Next
		End
		If fadeMusic Then
			SetMusicVolume((ratio) * (game.musicVolume / 100.0))
		End
		if counter > fadeTime
			active = false
			if fadeOut
				game.currentScreen.PostFadeOut()
			else
				game.currentScreen.PostFadeIn()
			End
		End
	End

	Method CalcRatio:Void()
		ratio = counter/fadeTime
		If ratio < 0 Then ratio = 0
		If ratio > 1 Then ratio = 1
		If fadeOut Then
			ratio = 1 - ratio
		End
	End

	Method Render:Void()
		If Not active Return

		SetAlpha 1 - ratio
		SetColor 0, 0, 0
		DrawRect 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT
		SetAlpha 1
		SetColor 255, 255, 255
	End

End

Class ExitScreen Extends Screen
	Method New()
		name = "exit"
	End

	Method Start:Void()
		ExitApp()
	End

	Method Render:Void()
	End

	Method Update:Void()
	End
End

Class Screen Abstract
	Field name$ = ""

	Method PreStart:Void()
		game.currentScreen = self
		Start()
	End

	Method Start:Void() Abstract

	Method Render:Void() Abstract

	Method Update:Void() Abstract

	Method PostFadeOut:Void()
		game.nextScreen.PreStart()
	End

	Method PostFadeIn:Void()
	End

	Method ExtraRender:Void()
	End

End Class

Class FPSCounter Abstract
	Global fpsCount:Int
	Global startTime:Int
	Global totalFPS:Int

	Function Update:Void()
		If Millisecs() - startTime >= 1000
			totalFPS = fpsCount
			fpsCount = 0
			startTime = Millisecs()
		Else
			fpsCount+=1
		End
	End

	Function Draw:Void(x% = 0, y% = 0, ax# = 0, ay# = 0)
		DrawText("FPS: " + totalFPS, x, y, ax, ay)
	End
End

' From James Boyd
Class DeltaTimer
	Field targetfps:Float = 60
	Field currentticks:Float
	Field lastticks:Float
	Field frametime:Float
	Field delta:Float

	Method New (fps:Float)
		targetfps = fps
		lastticks = Millisecs()
	End

	Method UpdateDelta:Void()
		currentticks = Millisecs()
		frametime = currentticks - lastticks
		delta = frametime / (1000.0 / targetfps)
		lastticks = currentticks
	End
End

Class ImageBank Extends StringMap<GameImage>

	Field path$ = "graphics/"

	Method Load:Void(name:String, nameoverride:String = "", midhandle:bool=true)
		Local i:GameImage = New GameImage
		i.Load(path + name, midhandle)

		If nameoverride <> "" Then i.name = nameoverride.ToUpper()
		Self.Set(i.name , i)
	End

	Method LoadAnim:Void(name:String, w%, h%, total%, tmpImage:Image, midhandle:Bool=true)
		Local i:GameImage = New GameImage
		i.LoadAnim(path + name, w, h, total, tmpImage, midhandle)
		Self.Set(i.name, i)
	End

	Method Find:GameImage(name:String)
		name = name.ToUpper()

		' debug: print all keys in the map
	'	For Local key:String = EachIn self.Keys()
	'		Print key + " is stored in the map."
	'	Next

		Local i:GameImage =  self.Get(name)
		AssertNotNull(i, "Image '" + name + "' not found in the ImageBank")
		Return i
	End

	Method PreCache:Void()
		Local gi:GameImage
		For Local key:String = EachIn self.Keys()
			gi = Self.Get(key)
			gi.PreCache()
		Next
	End

End

Class GameImage
	Field name$
	Field image:Image
	Field w%
	Field h%
	Field w2#
	Field h2#
	Field midhandled%=0

	Field leftMargin%=0
	Field rightMargin%=0
	Field topMargin%=0
	Field bottomMargin%=0

	Method Load:Void(file$, midhandle:bool=true)
		name = StripAll(file.ToUpper())
		image = LoadBitmap(file)
		CalcSize()
		MidHandle(midhandle)
	End

	Method LoadAnim:Void(file$, w%, h%, total%, tmpImage:Image, midhandle:Bool=true)
		name = StripAll(file.ToUpper())
		image = LoadAnimBitmap(file, w, h, total, tmpImage)
		CalcSize()
		MidHandle(midhandle)
	End

	Method CalcSize:Void()
		If image <> Null Then
			w = image.Width()
			h = image.Height()
			w2 = w/2
			h2 = h/2
		End
	End

	Method MidHandle:Void(On:Bool)
		If On Then
			image.SetHandle(w2, h2)
			midhandled=1
		Else
			image.SetHandle(0, 0)
			midhandled=0
		End
	End

	Method Draw:Void(x#, y#, rotation# = 0, scaleX# = 1, scaleY# = 1, frame% = 0)
		DrawImage(self.image, x, y, rotation, scaleX, scaleY, frame)
	End

	Method DrawTiled:Void(x#, y#, w#, h#, scaleX# = 1, scaleY# = 1, frame% = 0)
	End

	Method DrawStretched:Void(x#, y#, rw#, rh#, frame% = 0)
	End

	Method DrawGrid:Void(x#, y#, rw#, rh#, frame% = 0)
		' draw top left corner
		DrawImageRect(self.image, x, y, 0, 0, leftMargin, topMargin, frame)
		' draw top right corner
		DrawImageRect(self.image, x+rw-rightMargin, y, w-rightMargin, 0, rightMargin, topMargin, frame)
		' draw bottom left corner
		DrawImageRect(self.image, x, y+rh-bottomMargin, 0, h-bottomMargin, leftMargin, bottomMargin, frame)
		' draw bottom right corner
		DrawImageRect(self.image, x+rw-rightMargin, y+rh-bottomMargin, w-rightMargin, h-bottomMargin, rightMargin, bottomMargin, frame)

		' work out how many horizontal and vertical tiles
		Local tileWidth% = (w-leftMargin-rightMargin)
		Local tileHeight% = (h-topMargin-bottomMargin)
		Local tileXCount% = (rw-leftMargin-rightMargin) / tileWidth
		Local tileYCount% = (rh-topMargin-bottomMargin) / tileHeight
		Local tileXOverflow% = (rw-leftMargin-rightMargin) Mod tileWidth
		Local tileYOverflow% = (rh-topMargin-bottomMargin) Mod tileHeight

		' tile top and bottom edges
		For Local i% = 0 Until tileXCount
			DrawImageRect(self.image, leftMargin+i*tileWidth,0,leftMargin,0,tileWidth,topMargin,frame)
			DrawImageRect(self.image, leftMargin+i*tileWidth,rh-bottomMargin,leftMargin,h-bottomMargin,tileWidth,bottomMargin,frame)
		Next
		If tileXOverflow > 0 Then
			DrawImageRect(self.image, leftMargin+tileXCount*tileWidth,0,leftMargin,0,tileXOverflow,topMargin,frame)
			DrawImageRect(self.image, leftMargin+tileXCount*tileWidth,rh-bottomMargin,leftMargin,h-bottomMargin,tileXOverflow,bottomMargin,frame)
		End

		' tile left and right edges
		For Local i% = 0 Until tileYCount
			DrawImageRect(self.image, 0, topMargin+i*tileHeight,0,topMargin,leftMargin,tileHeight,frame)
			DrawImageRect(self.image, rw-rightMargin,topMargin+i*tileHeight,w-rightMargin,topMargin,rightMargin,tileHeight,frame)
		Next
		If tileYOverflow > 0 Then
			DrawImageRect(self.image, 0, topMargin+tileYCount*tileHeight,0,topMargin,leftMargin,tileYOverflow,frame)
			DrawImageRect(self.image, rw-rightMargin,topMargin+tileYCount*tileHeight,w-rightMargin,topMargin,rightMargin,tileYOverflow,frame)
		End

		' tile centre
		' TODO
	End

	Method PreCache:Void()
		DrawImage self.image, -self.w-50, -self.h-50
	End
End

Class SoundBank Extends StringMap<GameSound>

	Global path$ = "sounds/"

	Method Load:Void(name:String, nameoverride:String = "")
		Local i:GameSound = New GameSound
		i.Load(name)

		If nameoverride <> "" Then i.name = nameoverride.ToUpper()
		Self.Set(i.name , i)
	End

	Method Find:GameSound(name:String)
		name = name.ToUpper()

		' debug: print all keys in the map
	'	For Local key:String = EachIn self.Keys()
	'		Print key + " is stored in the map."
	'	Next

		Local i:GameSound =  Self.Get(name)
		AssertNotNull(i, "Sound '" + name + "' not found in the SoundBank")
		Return i
	End
End

Class GameSound
	Field name$
	Field sound:Sound
	Field rate# = 1
	Field pan# = 0
	Field volume# = 1
	Field loop% = 0

	Method Load:Void(file$)

		If file.Contains(".wav") Or file.Contains(".ogg") Or file.Contains(".mp3") Then
			sound = LoadSoundSample(SoundBank.path + file)
		Else
			#if TARGET="flash"
				sound = LoadSoundSample(SoundBank.path + file +".mp3")
			#else If TARGET="android"
				sound = LoadSoundSample(SoundBank.path + file +".ogg")
			#else
				sound = LoadSoundSample(SoundBank.path + file +".wav")
			#endif
		End

		name = StripAll(file.ToUpper())
	End

	Method Play:Void()
		SoundPlayer.PlayFx(sound, pan, rate, volume * (game.soundVolume / 100.0), loop)
	End
End

Class SoundPlayer
	Global channel:Int
	Const MAX_CHANNELS:Int = 31

	Function PlayFx:Void(s:Sound, pan#=0, rate#=1, volume#=1, loop% = 0)
		channel += 1
		If (channel > MAX_CHANNELS) Then channel = 0

		StopChannel(channel)
		PlaySound(s, channel, loop)
		SetChannelPan(channel, pan)
		SetChannelRate(channel, rate)
		SetChannelVolume(channel, volume)
	End
End

Class Sprite
	Field name$
	Field visible?
	Field x#, y#
	Field ox#, oy#
	Field dx#, dy#
	Field speedX#, speedY#, speed#
	Field maxXSpeed#, maxYSpeed#
	Field image:GameImage
	Field scaleX# = 1, scaleY# = 1

	Field red% = 255, green% = 255, blue% = 255, alpha# = 1
	Field hitBoxX:Int = 0
	Field hitBoxY:Int = 0
	Field hitBoxWidth:Int
	Field hitBoxHeight:Int

	' Animation
	Field frame:Int
	Field frameTimer:Int
	Field frameStart:Int
	Field frameEnd:Int
	Field frameSpeed:Int = 0
	Field reverse:Bool = false
	Field pingPong:Bool = false
	Field loop:Bool = true
	Field ping%

	' Scale
	Field scaleCounter#=0
	Field scaleXSpeed# = 0.1
	Field scaleYSpeed# = 0.1
	Field ygravity#
	Field maxFrame:Int

	' Rotation
	Field rotationCounter#=0
	Field rotationLength%=1000
	Field rotationLoop% = 0
	Field rotation#, rotationSpeed# = 1

	Method New(img:GameImage,x#, y#)
		Self.image = img
		Self.x = x
		Self.y = y
		self.alpha = 1
		self.SetHitBox(-img.w2, -img.h2, img.w, img.h)
		Self.visible = True
	End

	Method SetImage:Void(gi:GameImage)
		image = gi
		GetSize()
	End

	Method GetSize:Void()
		'Sets w and h based on the current image.
		If image <> Null Then
			image.w = image.image.Width()
			image.h = image.image.Height()
			image.w2 = image.w/2
			image.h2 = image.h/2
		End
	End

	Method Precache:Void()
		DrawImage image.image, -image.w-50, -image.h-50
	End

	Method SetRGB:Void(r%,g%,b%)
		Self.red = r
		Self.green = g
		Self.blue = b
	End

	Method SetScaleXY:Void(sx#, sy#)
		self.scaleX = sx
		self.scaleY = sy
	End

	Method Move:Void()
		Self.x+=Self.dx * dt.delta
		Self.y+=Self.dy * dt.delta

		If ygravity > 0 Then dy += ygravity * dt.delta
	End

	Method ManageScale:Void()
		If scaleCounter>0 Then
			scaleCounter-=1*dt.delta
			scaleX+=scaleXSpeed*dt.delta
			scaleY+=scaleYSpeed*dt.delta
		End
	End

	Method ManageRotation:Void()
		If rotationLoop Then
			rotation+=rotationSpeed*dt.delta
			If rotation >= 360 Then rotation-=360
			If rotation <0 Then rotation+=360
		Else
			If rotationCounter>0 Then
				rotationCounter-=1 * dt.delta
				rotation+=rotationSpeed * dt.delta
			End
		End
	End

	Method MoveForward:Void()
		dx = -Sin(rotation) * speed
        dy = -Cos(rotation) * speed

		move()
	End

	Method SetFrame:Void(startFrame:Int=0, endFrame:Int=0, speed:Int=125, pingPong:Bool = false, loop:Bool = true)
		frame = startFrame
		frameStart = startFrame
		frameEnd = endFrame
		if startFrame > endFrame
			reverse = true
		else
			reverse = false
		End
		self.pingPong = pingPong
		self.loop = loop
		frameSpeed = speed
		frameTimer = Millisecs()
		ping = 0
	End

	Method UpdateAnimation:Void()
		if frameSpeed > 0
			If Millisecs() > frameTimer + frameSpeed
				if not reverse
					frame+=1
					If frame > frameEnd
						ResetAnim()
					End
				else
					frame-=1
					If frame < frameEnd
						ResetAnim()
					End
				End
				frameTimer = Millisecs()
			End
		end
	End

	Method ResetAnim:Void()
		if loop then
			if pingPong
				reverse = Not reverse
				frame = frameEnd
				local ts% = frameStart
				frameStart = frameEnd
				frameEnd = ts
			else
				frame = frameStart
			End
		else
			if pingPong and ping <1
				reverse = Not reverse
				frame = frameEnd
				local ts% = frameStart
				frameStart = frameEnd
				frameEnd = ts
				ping+=1
			else
				frame = frameEnd
			End
		End
	End

	Method Draw:Void()
		Draw(0,0)
	End

	Method Draw:Void(offsetx#=0, offsety#=0)
		If x - offsetx + image.w < 0 Or x - offsetx - image.w >= SCREEN_WIDTH Or y - offsety + image.h < 0 Or y - offsety - image.h >= SCREEN_HEIGHT Then Return
		If Self.alpha > 1 Then Self.alpha = 1
		If Self.alpha < 0 Then Self.alpha = 0
		SetAlpha Self.alpha
		SetColor red, green, blue ' doesnt work with images!?!??!
		DrawImage(image.image, x - offsetx, y - offsety, rotation, scaleX, scaleY, frame)
		SetColor 255, 255, 255
		SetAlpha 1

	'	if debug
	'		drawRectOutline(x + hitBoxX, y + hitBoxY, hitBoxWidth, hitBoxHeight)
	'	End
	End

	Method SetupRotation:Void(rotationSpeed:Float, length:Int, loop:Bool = False, rndPosition:Bool = False)
		Self.rotationSpeed = rotationSpeed
		If loop Then
			rotationLoop = 1
			If rndPosition Then rotation = Rnd(0,360)
		Else
			rotationLength = length
			rotationCounter = rotationLength
		End
	End

	Method Collide:Int(sprite:Sprite)
		Return RectsOverlap(x + hitBoxX, y + hitBoxY, hitBoxWidth, hitBoxHeight,
							sprite.x + sprite.hitBoxX, sprite.y + sprite.hitBoxY, sprite.hitBoxWidth, sprite.hitBoxHeight)
	End

	Method SetHitBox:Void(hitX:Int, hitY:Int, hitWidth:Int, hitHeight:Int)
		hitBoxX = hitX
		hitBoxY = hitY
		hitBoxWidth = hitWidth
		hitBoxHeight = hitHeight
	End
End

Class Particle Extends Sprite
	Global MAX_PARTICLES% = 800
	Global particles:Particle[MAX_PARTICLES]
	Global lastDeath% = 0
	Global maxIndex% = -1
	Global minIndex% = -1
	Global particleCount% = 0
	Field lifeCounter# = 0
	Field fadeIn# = 0
	Field fadeCounter#
	Field fadeInLength# = 0
	Field fadeLength#=0
	Field active% = 0

	Function Cache:Void()
		For Local i% = 0 to MAX_PARTICLES - 1
			particles[i] = New Particle()
		Next
	End

	Function Create:Particle(gi:GameImage, x#, y#, dx#=0, dy#=0, gravity#=0, fadeLength#=0, lifeCounter%=0)
		Local i%=lastDeath
		Repeat
			If particles[i] = Null Then particles[i] = New Particle()
			If Not particles[i].active Then
				particles[i].SetImage(gi)
				particles[i].x = x
				particles[i].y = y
				particles[i].dx = dx
				particles[i].dy = dy

				particles[i].ygravity = gravity
				particles[i].fadeLength = fadeLength / 10
				particles[i].fadeCounter = particles[i].fadeLength
				If lifeCounter>0 Then particles[i].lifeCounter = lifeCounter / 10
				particles[i].active = 1
				If maxIndex < 0 Or i > maxIndex Then maxIndex = i
				If minIndex < 0 Or i < minIndex Then minIndex = i
				particleCount += 1
				Return particles[i]
			End
			i += 1
			If i >= MAX_PARTICLES Then i = 0
		Until i = lastDeath
		Return Null
	End

	Function Clear:Void()
		For Local i% = 0 to MAX_PARTICLES - 1
			particles[i].alpha = 0
			particles[i].active = False
		Next
		minIndex = -1
		maxIndex = -1
		particleCount = 0
		lastDeath = 0
	End

	Function DrawAll:Void()
		If minIndex < 0 Or maxIndex < 0 Then Return
		For Local i% = minIndex to maxIndex
			if particles[i] <> null And particles[i].image <> null
				If particles[i].fadeCounter > 0 and particles[i].active Then
					If particles[i].fadeIn Then
						particles[i].alpha = particles[i].fadeCounter/particles[i].fadeInLength
					Else
						particles[i].alpha = particles[i].fadeCounter/particles[i].fadeLength
					End
				End
				particles[i].Draw()
			End
		Next
	End

	Function UpdateAll:Void()
		If minIndex < 0 Or maxIndex < 0 Then Return
		Local newMinIndex% = -1
		Local newMaxIndex% = -1
		For Local i% = minIndex to maxIndex
			if particles[i] <> null And particles[i].image <> null
				If particles[i].active
					particles[i].Update()
					If particles[i].active Then
						If newMinIndex < 0 Then newMinIndex = i
						newMaxIndex = i
					Else
						lastDeath = i
						particleCount -= 1
					End
				End
			End
		Next
		minIndex = newMinIndex
		maxIndex = newMaxIndex
	End

	Method Update:Void()
		Super.Move()

		If fadeIn Then
			fadeCounter+=dt.delta

			If fadeCounter >= fadeInLength Then
				fadeCounter = fadeLength
				fadeIn = 0
				alpha = 1
			End
		ElseIf fadeCounter>0 Then
			fadeCounter-=dt.delta
			If fadeCounter <= 0 Then
				alpha = 0
				active = 0
			End
		End
	End

End

Strict

Import mojo
Import functions

'Device width and height
Global DEVICE_WIDTH%
Global DEVICE_HEIGHT%

' Screen width and height
Global SCREEN_WIDTH#
Global SCREEN_HEIGHT#

' Half of SCREEN_WIDTH and HEIGHT
Global SCREEN_WIDTH2#
Global SCREEN_HEIGHT2#

' Used for Virtual Res
Global SCREENX_RATIO# = 1
Global SCREENY_RATIO# = 1

' THE GAME!!
Global game:DiddyApp

' Used for delta timing movement
Global dt:DeltaTimer

Class DiddyApp Extends App

	Field debugOn:Bool = False
	Field drawFPSOn:Bool = False

	Field virtualResOn:Bool = True

	Field FPS% = 60

	' current Screen
	Field currentScreen:Screen
	' next Screen
	Field nextScreen:Screen
	' exit Screen
	Field exitScreen:ExitScreen = new ExitScreen()
	' used for fading
	Field screenFade:ScreenFade = New ScreenFade

	' mouse
	Field mouseX:Int, mouseY:Int
	Field mouseHit:Int

	' Store the images here
	Global images:ImageBank = New ImageBank
	' Store the sounds here
	Global sounds:SoundBank = New SoundBank
	' volume control
	Field soundVolume:Int = 100
	Field musicVolume:Int = 100
	Field musicOkay:Int

	Method OnCreate:Int()
		' Store the device width and height
		DEVICE_WIDTH = DeviceWidth()
		DEVICE_HEIGHT = DeviceHeight()

		SetScreenSize(DEVICE_WIDTH, DEVICE_HEIGHT)

		' set the mouse x,y
		mouseX = MouseX() / SCREENX_RATIO
		mouseY = MouseY() / SCREENY_RATIO

		' Set the Random seed
		Seed = RealMillisecs()
		' Create the delta timer
		dt = New DeltaTimer(FPS)
		SetUpdateRate FPS

		'create all the particles
		Particle.Cache()

		Return 0
	End

	Method SetScreenSize:Void(w:Float, h:Float)
		SCREEN_WIDTH = w
		SCREEN_HEIGHT = h
		SCREEN_WIDTH2 = SCREEN_WIDTH / 2
		SCREEN_HEIGHT2 = SCREEN_HEIGHT / 2

		SCREENX_RATIO = DEVICE_WIDTH/SCREEN_WIDTH
		SCREENY_RATIO = DEVICE_HEIGHT/SCREEN_HEIGHT

		If SCREENX_RATIO <> 1 Or SCREENY_RATIO <> 1
			virtualResOn = True
		End
	End

	Method OnRender:Int()
		FPSCounter.Update()
		If virtualResOn
			PushMatrix
			Scale SCREENX_RATIO, SCREENY_RATIO
		End
			currentScreen.Render()
			If screenFade.active then screenFade.Render()
		if virtualResOn
			PopMatrix
		End
		currentScreen.ExtraRender()
		If debugOn
			DrawDebug()
		End
		If drawFPSOn
			DrawFPS()
		End
		Return 0
	End

	Method OnUpdate:Int()
		dt.UpdateDelta()
		ScreenLogic()

		Return 0
	End

	Method ScreenLogic:Int()
		mouseX = MouseX() / SCREENX_RATIO
		mouseY = MouseY() / SCREENY_RATIO
		mouseHit = MouseHit()

		If screenFade.active then screenFade.Update()
		currentScreen.Update()
	End

	Method DrawDebug:Void()
		SetColor 255, 255, 255
		FPSCounter.Draw(0,0)
		DrawText "Screen         = "+currentScreen.name, 0, 10
		DrawText "Delta          = "+FormatNumber(dt.delta, 2) , 0, 20
		DrawText "Screen Width   = "+SCREEN_WIDTH, 0, 30
		DrawText "Screen Height  = "+SCREEN_HEIGHT, 0, 40
		DrawText "VMouseX        = "+Self.mouseX, 0, 50
		DrawText "VMouseY        = "+Self.mouseY, 0, 60
		DrawText "MouseX         = "+MouseX(), 0, 70
		DrawText "MouseY         = "+MouseY(), 0, 80
		DrawText "MusicOkay      = "+musicOkay, 0, 90
		DrawText "Music State    = "+MusicState(), 0, 100
		DrawText "Music Volume   = "+Self.musicVolume, 0, 110
		DrawText "Sound Volume   = "+Self.soundVolume, 0, 120
		DrawText "Sound Channel  = "+SoundPlayer.channel, 0, 130
	End

	Method DrawFPS:Void()
		DrawText FPSCounter.totalFPS, 0, 0
	End

	Method MusicPlay:Void(file:String, flags:Int=1)
		SetMusicVolume(musicVolume/100.0)
		musicOkay = PlayMusic("music/"+file, flags)
	End

	Method MusicSetVolume:Void(volume:Int)
		If volume < 0 Then volume = 0
		If volume > 100 Then volume = 100
		Self.musicVolume = volume
		SetMusicVolume(musicVolume/100.0)
	End

	Method SoundSetVolume:Void(volume:Int)
		If volume < 0 Then volume = 0
		If volume > 100 Then volume = 100
		Self.soundVolume = volume
		For Local i% = 0 To SoundPlayer.MAX_CHANNELS
			SetChannelVolume(i, game.soundVolume / 100.0)
		Next
	End

End

Class ScreenFade
	Field fadeTime:Float
	Field fadeOut:Bool
	Field ratio:Float = 0
	Field active:Bool
	Field counter:Float
	Field fadeMusic:Bool
	Field fadeSound:Bool

	Method Start:Void(fadeTime:Float, fadeOut:Bool, fadeSound:Bool = False, fadeMusic:Bool = False)
		If active Then Return
		active = True
		Self.fadeTime = fadeTime
		Self.fadeOut = fadeOut
		Self.fadeMusic = fadeMusic
		Self.fadeSound = fadeSound
		If fadeOut Then
			ratio = 1
		Else
			ratio = 0
		End
		counter = 0
	End

	Method Update:Void()
		If Not active Return
		counter += dt.delta
		CalcRatio()
		If fadeSound Then
			For Local i% = 0 To SoundPlayer.MAX_CHANNELS
				SetChannelVolume(i, (ratio) * (game.soundVolume / 100.0))
			Next
		End
		If fadeMusic Then
			SetMusicVolume((ratio) * (game.musicVolume / 100.0))
		End
		if counter > fadeTime
			active = false
			if fadeOut
				game.currentScreen.PostFadeOut()
			else
				game.currentScreen.PostFadeIn()
			End
		End
	End

	Method CalcRatio:Void()
		ratio = counter/fadeTime
		If ratio < 0 Then ratio = 0
		If ratio > 1 Then ratio = 1
		If fadeOut Then
			ratio = 1 - ratio
		End
	End

	Method Render:Void()
		If Not active Return

		SetAlpha 1 - ratio
		SetColor 0, 0, 0
		DrawRect 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT
		SetAlpha 1
		SetColor 255, 255, 255
	End

End

Class ExitScreen Extends Screen
	Method New()
		name = "exit"
	End

	Method Start:Void()
		ExitApp()
	End

	Method Render:Void()
	End

	Method Update:Void()
	End
End

Class Screen Abstract
	Field name$ = ""

	Method PreStart:Void()
		game.currentScreen = self
		Start()
	End

	Method Start:Void() Abstract

	Method Render:Void() Abstract

	Method Update:Void() Abstract

	Method PostFadeOut:Void()
		game.nextScreen.PreStart()
	End

	Method PostFadeIn:Void()
	End

	Method ExtraRender:Void()
	End

End Class

Class FPSCounter Abstract
	Global fpsCount:Int
	Global startTime:Int
	Global totalFPS:Int

	Function Update:Void()
		If Millisecs() - startTime >= 1000
			totalFPS = fpsCount
			fpsCount = 0
			startTime = Millisecs()
		Else
			fpsCount+=1
		End
	End

	Function Draw:Void(x% = 0, y% = 0, ax# = 0, ay# = 0)
		DrawText("FPS: " + totalFPS, x, y, ax, ay)
	End
End

' From James Boyd
Class DeltaTimer
	Field targetfps:Float = 60
	Field currentticks:Float
	Field lastticks:Float
	Field frametime:Float
	Field delta:Float

	Method New (fps:Float)
		targetfps = fps
		lastticks = Millisecs()
	End

	Method UpdateDelta:Void()
		currentticks = Millisecs()
		frametime = currentticks - lastticks
		delta = frametime / (1000.0 / targetfps)
		lastticks = currentticks
	End
End

Class ImageBank Extends StringMap<GameImage>

	Field path$ = "graphics/"

	Method Load:Void(name:String, nameoverride:String = "", midhandle:bool=true)
		Local i:GameImage = New GameImage
		i.Load(path + name, midhandle)

		If nameoverride <> "" Then i.name = nameoverride.ToUpper()
		Self.Set(i.name , i)
	End

	Method LoadAnim:Void(name:String, w%, h%, total%, tmpImage:Image, midhandle:Bool=true)
		Local i:GameImage = New GameImage
		i.LoadAnim(path + name, w, h, total, tmpImage, midhandle)
		Self.Set(i.name, i)
	End

	Method Find:GameImage(name:String)
		name = name.ToUpper()

		' debug: print all keys in the map
	'	For Local key:String = EachIn self.Keys()
	'		Print key + " is stored in the map."
	'	Next

		Local i:GameImage =  self.Get(name)
		AssertNotNull(i, "Image '" + name + "' not found in the ImageBank")
		Return i
	End

	Method PreCache:Void()
		Local gi:GameImage
		For Local key:String = EachIn self.Keys()
			gi = Self.Get(key)
			gi.PreCache()
		Next
	End

End

Class GameImage
	Field name$
	Field image:Image
	Field w%
	Field h%
	Field w2#
	Field h2#
	Field midhandled%=0

	Field leftMargin%=0
	Field rightMargin%=0
	Field topMargin%=0
	Field bottomMargin%=0

	Method Load:Void(file$, midhandle:bool=true)
		name = StripAll(file.ToUpper())
		image = LoadBitmap(file)
		CalcSize()
		MidHandle(midhandle)
	End

	Method LoadAnim:Void(file$, w%, h%, total%, tmpImage:Image, midhandle:Bool=true)
		name = StripAll(file.ToUpper())
		image = LoadAnimBitmap(file, w, h, total, tmpImage)
		CalcSize()
		MidHandle(midhandle)
	End

	Method CalcSize:Void()
		If image <> Null Then
			w = image.Width()
			h = image.Height()
			w2 = w/2
			h2 = h/2
		End
	End

	Method MidHandle:Void(On:Bool)
		If On Then
			image.SetHandle(w2, h2)
			midhandled=1
		Else
			image.SetHandle(0, 0)
			midhandled=0
		End
	End

	Method Draw:Void(x#, y#, rotation# = 0, scaleX# = 1, scaleY# = 1, frame% = 0)
		DrawImage(self.image, x, y, rotation, scaleX, scaleY, frame)
	End

	Method DrawTiled:Void(x#, y#, w#, h#, scaleX# = 1, scaleY# = 1, frame% = 0)
	End

	Method DrawStretched:Void(x#, y#, rw#, rh#, frame% = 0)
	End

	Method DrawGrid:Void(x#, y#, rw#, rh#, frame% = 0)
		' draw top left corner
		DrawImageRect(self.image, x, y, 0, 0, leftMargin, topMargin, frame)
		' draw top right corner
		DrawImageRect(self.image, x+rw-rightMargin, y, w-rightMargin, 0, rightMargin, topMargin, frame)
		' draw bottom left corner
		DrawImageRect(self.image, x, y+rh-bottomMargin, 0, h-bottomMargin, leftMargin, bottomMargin, frame)
		' draw bottom right corner
		DrawImageRect(self.image, x+rw-rightMargin, y+rh-bottomMargin, w-rightMargin, h-bottomMargin, rightMargin, bottomMargin, frame)

		' work out how many horizontal and vertical tiles
		Local tileWidth% = (w-leftMargin-rightMargin)
		Local tileHeight% = (h-topMargin-bottomMargin)
		Local tileXCount% = (rw-leftMargin-rightMargin) / tileWidth
		Local tileYCount% = (rh-topMargin-bottomMargin) / tileHeight
		Local tileXOverflow% = (rw-leftMargin-rightMargin) Mod tileWidth
		Local tileYOverflow% = (rh-topMargin-bottomMargin) Mod tileHeight

		' tile top and bottom edges
		For Local i% = 0 Until tileXCount
			DrawImageRect(self.image, leftMargin+i*tileWidth,0,leftMargin,0,tileWidth,topMargin,frame)
			DrawImageRect(self.image, leftMargin+i*tileWidth,rh-bottomMargin,leftMargin,h-bottomMargin,tileWidth,bottomMargin,frame)
		Next
		If tileXOverflow > 0 Then
			DrawImageRect(self.image, leftMargin+tileXCount*tileWidth,0,leftMargin,0,tileXOverflow,topMargin,frame)
			DrawImageRect(self.image, leftMargin+tileXCount*tileWidth,rh-bottomMargin,leftMargin,h-bottomMargin,tileXOverflow,bottomMargin,frame)
		End

		' tile left and right edges
		For Local i% = 0 Until tileYCount
			DrawImageRect(self.image, 0, topMargin+i*tileHeight,0,topMargin,leftMargin,tileHeight,frame)
			DrawImageRect(self.image, rw-rightMargin,topMargin+i*tileHeight,w-rightMargin,topMargin,rightMargin,tileHeight,frame)
		Next
		If tileYOverflow > 0 Then
			DrawImageRect(self.image, 0, topMargin+tileYCount*tileHeight,0,topMargin,leftMargin,tileYOverflow,frame)
			DrawImageRect(self.image, rw-rightMargin,topMargin+tileYCount*tileHeight,w-rightMargin,topMargin,rightMargin,tileYOverflow,frame)
		End

		' tile centre
		' TODO
	End

	Method PreCache:Void()
		DrawImage self.image, -self.w-50, -self.h-50
	End
End

Class SoundBank Extends StringMap<GameSound>

	Global path$ = "sounds/"

	Method Load:Void(name:String, nameoverride:String = "")
		Local i:GameSound = New GameSound
		i.Load(name)

		If nameoverride <> "" Then i.name = nameoverride.ToUpper()
		Self.Set(i.name , i)
	End

	Method Find:GameSound(name:String)
		name = name.ToUpper()

		' debug: print all keys in the map
	'	For Local key:String = EachIn self.Keys()
	'		Print key + " is stored in the map."
	'	Next

		Local i:GameSound =  Self.Get(name)
		AssertNotNull(i, "Sound '" + name + "' not found in the SoundBank")
		Return i
	End
End

Class GameSound
	Field name$
	Field sound:Sound
	Field rate# = 1
	Field pan# = 0
	Field volume# = 1
	Field loop% = 0

	Method Load:Void(file$)

		If file.Contains(".wav") Or file.Contains(".ogg") Or file.Contains(".mp3") Then
			sound = LoadSoundSample(SoundBank.path + file)
		Else
			#if TARGET="flash"
				sound = LoadSoundSample(SoundBank.path + file +".mp3")
			#else If TARGET="android"
				sound = LoadSoundSample(SoundBank.path + file +".ogg")
			#else
				sound = LoadSoundSample(SoundBank.path + file +".wav")
			#endif
		End

		name = StripAll(file.ToUpper())
	End

	Method Play:Void()
		SoundPlayer.PlayFx(sound, pan, rate, volume * (game.soundVolume / 100.0), loop)
	End
End

Class SoundPlayer
	Global channel:Int
	Const MAX_CHANNELS:Int = 31

	Function PlayFx:Void(s:Sound, pan#=0, rate#=1, volume#=1, loop% = 0)
		channel += 1
		If (channel > MAX_CHANNELS) Then channel = 0

		StopChannel(channel)
		PlaySound(s, channel, loop)
		SetChannelPan(channel, pan)
		SetChannelRate(channel, rate)
		SetChannelVolume(channel, volume)
	End
End

Class Sprite
	Field name$
	Field visible?
	Field x#, y#
	Field ox#, oy#
	Field dx#, dy#
	Field speedX#, speedY#, speed#
	Field maxXSpeed#, maxYSpeed#
	Field image:GameImage
	Field scaleX# = 1, scaleY# = 1

	Field red% = 255, green% = 255, blue% = 255, alpha# = 1
	Field hitBoxX:Int = 0
	Field hitBoxY:Int = 0
	Field hitBoxWidth:Int
	Field hitBoxHeight:Int

	' Animation
	Field frame:Int
	Field frameTimer:Int
	Field frameStart:Int
	Field frameEnd:Int
	Field frameSpeed:Int = 0
	Field reverse:Bool = false
	Field pingPong:Bool = false
	Field loop:Bool = true
	Field ping%

	' Scale
	Field scaleCounter#=0
	Field scaleXSpeed# = 0.1
	Field scaleYSpeed# = 0.1
	Field ygravity#
	Field maxFrame:Int

	' Rotation
	Field rotationCounter#=0
	Field rotationLength%=1000
	Field rotationLoop% = 0
	Field rotation#, rotationSpeed# = 1

	Method New(img:GameImage,x#, y#)
		Self.image = img
		Self.x = x
		Self.y = y
		self.alpha = 1
		self.SetHitBox(-img.w2, -img.h2, img.w, img.h)
		Self.visible = True
	End

	Method SetImage:Void(gi:GameImage)
		image = gi
		GetSize()
	End

	Method GetSize:Void()
		'Sets w and h based on the current image.
		If image <> Null Then
			image.w = image.image.Width()
			image.h = image.image.Height()
			image.w2 = image.w/2
			image.h2 = image.h/2
		End
	End

	Method Precache:Void()
		DrawImage image.image, -image.w-50, -image.h-50
	End

	Method SetRGB:Void(r%,g%,b%)
		Self.red = r
		Self.green = g
		Self.blue = b
	End

	Method SetScaleXY:Void(sx#, sy#)
		self.scaleX = sx
		self.scaleY = sy
	End

	Method Move:Void()
		Self.x+=Self.dx * dt.delta
		Self.y+=Self.dy * dt.delta

		If ygravity > 0 Then dy += ygravity * dt.delta
	End

	Method ManageScale:Void()
		If scaleCounter>0 Then
			scaleCounter-=1*dt.delta
			scaleX+=scaleXSpeed*dt.delta
			scaleY+=scaleYSpeed*dt.delta
		End
	End

	Method ManageRotation:Void()
		If rotationLoop Then
			rotation+=rotationSpeed*dt.delta
			If rotation >= 360 Then rotation-=360
			If rotation <0 Then rotation+=360
		Else
			If rotationCounter>0 Then
				rotationCounter-=1 * dt.delta
				rotation+=rotationSpeed * dt.delta
			End
		End
	End

	Method MoveForward:Void()
		dx = -Sin(rotation) * speed
        dy = -Cos(rotation) * speed

		move()
	End

	Method SetFrame:Void(startFrame:Int=0, endFrame:Int=0, speed:Int=125, pingPong:Bool = false, loop:Bool = true)
		frame = startFrame
		frameStart = startFrame
		frameEnd = endFrame
		if startFrame > endFrame
			reverse = true
		else
			reverse = false
		End
		self.pingPong = pingPong
		self.loop = loop
		frameSpeed = speed
		frameTimer = Millisecs()
		ping = 0
	End

	Method UpdateAnimation:Void()
		if frameSpeed > 0
			If Millisecs() > frameTimer + frameSpeed
				if not reverse
					frame+=1
					If frame > frameEnd
						ResetAnim()
					End
				else
					frame-=1
					If frame < frameEnd
						ResetAnim()
					End
				End
				frameTimer = Millisecs()
			End
		end
	End

	Method ResetAnim:Void()
		if loop then
			if pingPong
				reverse = Not reverse
				frame = frameEnd
				local ts% = frameStart
				frameStart = frameEnd
				frameEnd = ts
			else
				frame = frameStart
			End
		else
			if pingPong and ping <1
				reverse = Not reverse
				frame = frameEnd
				local ts% = frameStart
				frameStart = frameEnd
				frameEnd = ts
				ping+=1
			else
				frame = frameEnd
			End
		End
	End

	Method Draw:Void()
		Draw(0,0)
	End

	Method Draw:Void(offsetx#=0, offsety#=0)
		If x - offsetx + image.w < 0 Or x - offsetx - image.w >= SCREEN_WIDTH Or y - offsety + image.h < 0 Or y - offsety - image.h >= SCREEN_HEIGHT Then Return
		If Self.alpha > 1 Then Self.alpha = 1
		If Self.alpha < 0 Then Self.alpha = 0
		SetAlpha Self.alpha
		SetColor red, green, blue ' doesnt work with images!?!??!
		DrawImage(image.image, x - offsetx, y - offsety, rotation, scaleX, scaleY, frame)
		SetColor 255, 255, 255
		SetAlpha 1

	'	if debug
	'		drawRectOutline(x + hitBoxX, y + hitBoxY, hitBoxWidth, hitBoxHeight)
	'	End
	End

	Method SetupRotation:Void(rotationSpeed:Float, length:Int, loop:Bool = False, rndPosition:Bool = False)
		Self.rotationSpeed = rotationSpeed
		If loop Then
			rotationLoop = 1
			If rndPosition Then rotation = Rnd(0,360)
		Else
			rotationLength = length
			rotationCounter = rotationLength
		End
	End

	Method Collide:Int(sprite:Sprite)
		Return RectsOverlap(x + hitBoxX, y + hitBoxY, hitBoxWidth, hitBoxHeight,
							sprite.x + sprite.hitBoxX, sprite.y + sprite.hitBoxY, sprite.hitBoxWidth, sprite.hitBoxHeight)
	End

	Method SetHitBox:Void(hitX:Int, hitY:Int, hitWidth:Int, hitHeight:Int)
		hitBoxX = hitX
		hitBoxY = hitY
		hitBoxWidth = hitWidth
		hitBoxHeight = hitHeight
	End
End

Class Particle Extends Sprite
	Global MAX_PARTICLES% = 800
	Global particles:Particle[MAX_PARTICLES]
	Global lastDeath% = 0
	Global maxIndex% = -1
	Global minIndex% = -1
	Global particleCount% = 0
	Field lifeCounter# = 0
	Field fadeIn# = 0
	Field fadeCounter#
	Field fadeInLength# = 0
	Field fadeLength#=0
	Field active% = 0

	Function Cache:Void()
		For Local i% = 0 to MAX_PARTICLES - 1
			particles[i] = New Particle()
		Next
	End

	Function Create:Particle(gi:GameImage, x#, y#, dx#=0, dy#=0, gravity#=0, fadeLength#=0, lifeCounter%=0)
		Local i%=lastDeath
		Repeat
			If particles[i] = Null Then particles[i] = New Particle()
			If Not particles[i].active Then
				particles[i].SetImage(gi)
				particles[i].x = x
				particles[i].y = y
				particles[i].dx = dx
				particles[i].dy = dy

				particles[i].ygravity = gravity
				particles[i].fadeLength = fadeLength / 10
				particles[i].fadeCounter = particles[i].fadeLength
				If lifeCounter>0 Then particles[i].lifeCounter = lifeCounter / 10
				particles[i].active = 1
				If maxIndex < 0 Or i > maxIndex Then maxIndex = i
				If minIndex < 0 Or i < minIndex Then minIndex = i
				particleCount += 1
				Return particles[i]
			End
			i += 1
			If i >= MAX_PARTICLES Then i = 0
		Until i = lastDeath
		Return Null
	End

	Function Clear:Void()
		For Local i% = 0 to MAX_PARTICLES - 1
			particles[i].alpha = 0
			particles[i].active = False
		Next
		minIndex = -1
		maxIndex = -1
		particleCount = 0
		lastDeath = 0
	End

	Function DrawAll:Void()
		If minIndex < 0 Or maxIndex < 0 Then Return
		For Local i% = minIndex to maxIndex
			if particles[i] <> null And particles[i].image <> null
				If particles[i].fadeCounter > 0 and particles[i].active Then
					If particles[i].fadeIn Then
						particles[i].alpha = particles[i].fadeCounter/particles[i].fadeInLength
					Else
						particles[i].alpha = particles[i].fadeCounter/particles[i].fadeLength
					End
				End
				particles[i].Draw()
			End
		Next
	End

	Function UpdateAll:Void()
		If minIndex < 0 Or maxIndex < 0 Then Return
		Local newMinIndex% = -1
		Local newMaxIndex% = -1
		For Local i% = minIndex to maxIndex
			if particles[i] <> null And particles[i].image <> null
				If particles[i].active
					particles[i].Update()
					If particles[i].active Then
						If newMinIndex < 0 Then newMinIndex = i
						newMaxIndex = i
					Else
						lastDeath = i
						particleCount -= 1
					End
				End
			End
		Next
		minIndex = newMinIndex
		maxIndex = newMaxIndex
	End

	Method Update:Void()
		Super.Move()

		If fadeIn Then
			fadeCounter+=dt.delta

			If fadeCounter >= fadeInLength Then
				fadeCounter = fadeLength
				fadeIn = 0
				alpha = 1
			End
		ElseIf fadeCounter>0 Then
			fadeCounter-=dt.delta
			If fadeCounter <= 0 Then
				alpha = 0
				active = 0
			End
		End
	End

End


fix broken import in google payment provider

Strict

Private

' ============================
' == COMMERCIAL BREAK START ==
' ============================
'
' Tired of setting things up? Monkey-Wizard to the rescue!
' --> https://github.com/michaelcontento/monkey-wizard
'
' And the whole thing should be easy as:
' --> wizard GooglePayment ../myproject/myproject.build/android
'
' ============================
' ==  COMMERCIAL BREAK END  ==
' ============================
'
'
' 1) Ensure this two lines exists inside <manifest> of your AndroidManifest.xml
'
'     <uses-permission android:name="android.permission.INTERNET"></uses-permission>
'     <uses-permission android:name="com.android.vending.BILLING"></uses-permission>
'
' 2) Add this reciever inside <application> of your AndroidManifest.xml
'
'    <service android:name="net.robotmedia.billing.BillingService" />
'    <receiver android:name="net.robotmedia.billing.BillingReceiver">
'        <intent-filter>
'            <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
'            <action android:name="com.android.vending.billing.RESPONSE_CODE" />
'            <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
'        </intent-filter>
'    </receiver>
'
' 3) Copy the billing library from the following link into src/
'
'    https://github.com/robotmedia/AndroidBillingLibrary
'
' 4) Patch AndroidBillingLibrary/src/net/robotmedia/billing/BillingRequest.java
'    around line 220 to look like:
'
'    ## BEFORE
'
'    @Override
'    public void onResponseCode(ResponseCode response) {
'        super.onResponseCode(response);
'        if (response == ResponseCode.RESULT_OK) {
'            BillingController.onTransactionsRestored();
'        }
'    }
'
'    ## AFTER
'
'    @Override
'    public void onResponseCode(ResponseCode response) {
'        super.onResponseCode(response);
'        BillingController.onTransactionsRestored();
'    }

Import bono
Import bono.src.payment.paymentprovider
Import "native/paymentwrapper-google.android.java"

Private Extern

Class PaymentWrapper
    Method Init:Void(publicKey:String)
    Method Purchase:Void(productId:String)
    Method IsBought:Bool(id:String)
    Method IsPurchaseInProgress:Bool()
End

Public

Class PaymentProviderAndroidGoogle Implements PaymentProvider
    Private

    Field wrapper:PaymentWrapper

    Public

    Field publicKey:String

    Method IsProcessing:Bool()
        Initialize()
        Return wrapper.IsPurchaseInProgress()
    End

    Method Purchase:Void(id:String)
        Initialize()
        wrapper.Purchase(id)
    End

    Method IsPurchased:Bool(id:String)
        Initialize()
        Return wrapper.IsBought(id)
    End

    Private

    Method Initialize:Void()
        If wrapper Then Return

        If Not publicKey
            Throw New RuntimeException("Please configure " +
                "PaymentProviderAndroidGoogle.publicKey with your " +
                "public key first.")
        End

        wrapper = New PaymentWrapper()
        wrapper.Init(publicKey)
    End
End

Strict

Private

' ============================
' == COMMERCIAL BREAK START ==
' ============================
'
' Tired of setting things up? Monkey-Wizard to the rescue!
' --> https://github.com/michaelcontento/monkey-wizard
'
' And the whole thing should be easy as:
' --> wizard GooglePayment ../myproject/myproject.build/android
'
' ============================
' ==  COMMERCIAL BREAK END  ==
' ============================
'
'
' 1) Ensure this two lines exists inside <manifest> of your AndroidManifest.xml
'
'     <uses-permission android:name="android.permission.INTERNET"></uses-permission>
'     <uses-permission android:name="com.android.vending.BILLING"></uses-permission>
'
' 2) Add this reciever inside <application> of your AndroidManifest.xml
'
'    <service android:name="net.robotmedia.billing.BillingService" />
'    <receiver android:name="net.robotmedia.billing.BillingReceiver">
'        <intent-filter>
'            <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
'            <action android:name="com.android.vending.billing.RESPONSE_CODE" />
'            <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
'        </intent-filter>
'    </receiver>
'
' 3) Copy the billing library from the following link into src/
'
'    https://github.com/robotmedia/AndroidBillingLibrary
'
' 4) Patch AndroidBillingLibrary/src/net/robotmedia/billing/BillingRequest.java
'    around line 220 to look like:
'
'    ## BEFORE
'
'    @Override
'    public void onResponseCode(ResponseCode response) {
'        super.onResponseCode(response);
'        if (response == ResponseCode.RESULT_OK) {
'            BillingController.onTransactionsRestored();
'        }
'    }
'
'    ## AFTER
'
'    @Override
'    public void onResponseCode(ResponseCode response) {
'        super.onResponseCode(response);
'        BillingController.onTransactionsRestored();
'    }

Import bono
Import bono.src.payment
Import "native/paymentwrapper-google.android.java"

Private Extern

Class PaymentWrapper
    Method Init:Void(publicKey:String)
    Method Purchase:Void(productId:String)
    Method IsBought:Bool(id:String)
    Method IsPurchaseInProgress:Bool()
End

Public

Class PaymentProviderAndroidGoogle Implements PaymentProvider
    Private

    Field wrapper:PaymentWrapper

    Public

    Field publicKey:String

    Method IsProcessing:Bool()
        Initialize()
        Return wrapper.IsPurchaseInProgress()
    End

    Method Purchase:Void(id:String)
        Initialize()
        wrapper.Purchase(id)
    End

    Method IsPurchased:Bool(id:String)
        Initialize()
        Return wrapper.IsBought(id)
    End

    Private

    Method Initialize:Void()
        If wrapper Then Return

        If Not publicKey
            Throw New RuntimeException("Please configure " +
                "PaymentProviderAndroidGoogle.publicKey with your " +
                "public key first.")
        End

        wrapper = New PaymentWrapper()
        wrapper.Init(publicKey)
    End
End


ColorTween bug fixed

ColorTween bug fixed

Strict

Import flixel.system.tweens.flxtween
Import flixel.system.tweens.util.ease

Class ColorTween Extends FlxTween

	Field color:Int

	Field alpha:Float

	Field red:Int

	Field green:Int

	Field blue:Int

Private
	Field _startA:Float

	Field _startR:Float

	Field _startG:Float

	Field _startB:Float

	Field _rangeA:Float

	Field _rangeR:Float

	Field _rangeG:Float

	Field _rangeB:Float

Public
	Method New(complete:FlxTweenListener = Null, type:Int = -1)
		Super.New(0, type, complete)
		alpha = 1
	End Method

	Method Tween:Void(duration:Float, fromColor:Int, toColor:Int, fromAlpha:Float = 1, toAlpha:Float = 1, ease:FlxEaseFunction = Null)
		fromColor &= $FFFFFF
		toColor &= $FFFFFF
		color = fromColor

		red = (fromColor Shr 16) & $FF
		green = (fromColor Shr 8) & $FF
		blue = fromColor & $FF

		_startR = red / 255.0
		_startG = green / 255.0
		_startB = blue / 255.0

		_rangeR = ( ( (toColor Shr 16) & $FF) / 255.0) - _startR
		_rangeG = ( ( (toColor Shr 8) & $FF) / 255.0) - _startG
		_rangeB = ( (toColor & $FF) / 255.0) - _startB

		_startA = fromAlpha
		alpha = fromAlpha

		_rangeA = toAlpha - alpha

		_target = duration
		_ease = ease

		Start()
	End Method

	Method Update:Void()
		Super.Update()

		alpha = _startA + _rangeA * _t

		red = (_startR + _rangeR * _t) * 255
		green = (_startG + _rangeG * _t) * 255
		blue = (_startB + _rangeB * _t) * 255

		color = red Shl 16 | green Shl 8 | blue
	End Method

End Class

Strict

Import flixel.system.tweens.flxtween
Import flixel.system.tweens.util.ease

Class ColorTween Extends FlxTween

	Field color:Int

	Field alpha:Float

	Field red:Int

	Field green:Int

	Field blue:Int

Private
	Field _startA:Float

	Field _startR:Float

	Field _startG:Float

	Field _startB:Float

	Field _rangeA:Float

	Field _rangeR:Float

	Field _rangeG:Float

	Field _rangeB:Float

Public
	Method New(complete:FlxTweenListener = Null, type:Int = -1)
		Super.New(0, type, complete)
		alpha = 1
	End Method

	Method Tween:Void(duration:Float, fromColor:Int, toColor:Int, fromAlpha:Float = 1, toAlpha:Float = 1, ease:FlxEaseFunction = Null)
		fromColor &= $FFFFFF
		toColor &= $FFFFFF
		color = fromColor

		red = (fromColor Shr 16) & $FF
		green = (fromColor Shr 8) & $FF
		blue = fromColor & $FF

		_startR = red / 255.0
		_startG = green / 255.0
		_startB = blue / 255.0

		_rangeR = ( ( (toColor Shr 16) & $FF) / 255) - _startR
		_rangeG = ( ( (toColor Shr 8) & $FF) / 255) - _startG
		_rangeB = ( (toColor & $FF) / 255) - _startB

		_startA = fromAlpha
		alpha = fromAlpha

		_rangeA = toAlpha - alpha

		_target = duration
		_ease = ease

		Start()
	End Method

	Method Update:Void()
		Super.Update()

		alpha = _startA + _rangeA * _t

		red = (_startR + _rangeR * _t) * 255
		green = (_startG + _rangeG * _t) * 255
		blue = (_startB + _rangeB * _t) * 255

		color = red Shl 16 | green Shl 8 | blue
	End Method

End Class


add SoundManager.newSoundCanStopOldest with default True

Strict

Private

Import bono
Import mojo.audio

Public

Class SoundManager
    Private

    Field sounds:StringMap<Sound> = New StringMap<Sound>()
    Field files:StringMap<String> = New StringMap<String>()
    Field extensions:String[] = ["mp3"]
    Field loopEnabled:StringMap<Bool> = New StringMap<Bool>()
    Field musicSounds:StringMap<Bool> = New StringMap<Bool>()
    Field lastChannel:Int
    Const CHANNELS:Int = 9

    Public

    Field failOnLoadingError:Bool = True
    Field newSoundCanStopOldest:Bool = True

    Method Add:Void(key:String, file:String)
        If files.Contains(key) Then Error("Key " + key + " already used")
        files.Set(key, file)
    End

    Method AddMusic:Void(key:String, file:String)
        Add(key, file)
        musicSounds.Set(key, True)
    End

    Method PreloadAll:Void()
        Local tmpSound:Sound
        For Local node:map.Node<String, String> = EachIn files
            If sounds.Contains(node.Key()) Then Continue
            If musicSounds.Contains(node.Key()) Then Continue

            tmpSound = LoadSound(node.Value())
            If tmpSound = Null Then Continue

            sounds.Set(node.Key(), LoadSound(node.Value()))
        End
    End

    Method LoopEnable:Void(key:String)
        loopEnabled.Set(key, True)
    End

    Method LoopDisable:Void(key:String)
        loopEnabled.Remove(key)
    End

    Method Play:Void(key:String)
        If musicSounds.Contains(key)
            PlayMusic(key)
        Else
            PlaySound(key)
        End
    End

    Method Pause:Void()
        PauseMusic()
        PauseSounds()
    End

    Method PauseMusic:Void()
        audio.PauseMusic()
    End

    Method PauseSounds:Void()
        For Local i := 0 To CHANNELS
            PauseChannel(i)
        End
    End

    Method Resume:Void()
        ResumeMusic()
        ResumeSounds()
    End

    Method ResumeMusic:Void()
        audio.ResumeMusic()
    End

    Method ResumeSounds:Void()
        For Local i := 0 To CHANNELS
            ResumeChannel(i)
        End
    End

    Private

    Method PlayMusic:Void(key:String)
        Local isLoop:Bool = loopEnabled.Contains(key)
        Local file:String = files.Get(key)

        For Local idx:Int = 0 Until extensions.Length()
            audio.PlayMusic(file + "." + extensions[idx], isLoop)
            If MusicState() = 1 Then Return
        End
    End

    Method PlaySound:Void(key:String)
        If Not sounds.Contains(key) Then Return

        Local channel:Int = GetFreeChannel()
        If channel = -1 Then Return

        audio.PlaySound(sounds.Get(key), channel, loopEnabled.Contains(key))
    End

    Method GetFreeChannel:Int()
        Local channelsChecked:Int = 0
        Local channelId:Int = lastChannel

        Repeat
            channelId += 1
            If channelId >= CHANNELS Then channelId = 0

            ' Skip the ChannelState check on Android, because:
            ' > Android: ChannelState always returns -1, ie: 'unknown'. Sounds
            ' >          to be used with PlaySound must be less than 1MB in
            ' >          length. Longer sounds can be played using the music
            ' >          commands.
            ' Source: http://blitz-wiki.appspot.com/mojo.audio
            If Target.IS_ANDROID Or ChannelState(channelId) = 0
                lastChannel = channelId
                Return channelId
            End

            channelsChecked += 1
        Until channelsChecked >= CHANNELS

        If Not newSoundCanStopOldest Then Return -1

        lastChannel += 1
        If lastChannel >= CHANNELS Then lastChannel = 0
        StopChannel(lastChannel)
        Return lastChannel
    End

    Method LoadSound:Sound(file:String)
        Local tmpSound:Sound
        For Local idx:Int = 0 Until extensions.Length()
            tmpSound = audio.LoadSound(file + "." + extensions[idx])
            If Not (tmpSound = Null) Then Return tmpSound
        End

        If failOnLoadingError Then Error("Unable to load sound file: " + file)
        Return Null
    End
End

Strict

Private

Import bono
Import mojo.audio

Public

Class SoundManager
    Private

    Field sounds:StringMap<Sound> = New StringMap<Sound>()
    Field files:StringMap<String> = New StringMap<String>()
    Field extensions:String[] = ["mp3"]
    Field loopEnabled:StringMap<Bool> = New StringMap<Bool>()
    Field musicSounds:StringMap<Bool> = New StringMap<Bool>()
    Field lastChannel:Int
    Const CHANNELS:Int = 9

    Public

    Field failOnLoadingError:Bool = True

    Method Add:Void(key:String, file:String)
        If files.Contains(key) Then Error("Key " + key + " already used")
        files.Set(key, file)
    End

    Method AddMusic:Void(key:String, file:String)
        Add(key, file)
        musicSounds.Set(key, True)
    End

    Method PreloadAll:Void()
        Local tmpSound:Sound
        For Local node:map.Node<String, String> = EachIn files
            If sounds.Contains(node.Key()) Then Continue
            If musicSounds.Contains(node.Key()) Then Continue

            tmpSound = LoadSound(node.Value())
            If tmpSound = Null Then Continue

            sounds.Set(node.Key(), LoadSound(node.Value()))
        End
    End

    Method LoopEnable:Void(key:String)
        loopEnabled.Set(key, True)
    End

    Method LoopDisable:Void(key:String)
        loopEnabled.Remove(key)
    End

    Method Play:Void(key:String)
        If musicSounds.Contains(key)
            PlayMusic(key)
        Else
            PlaySound(key)
        End
    End

    Method Pause:Void()
        PauseMusic()
        PauseSounds()
    End

    Method PauseMusic:Void()
        audio.PauseMusic()
    End

    Method PauseSounds:Void()
        For Local i := 0 To CHANNELS
            PauseChannel(i)
        End
    End

    Method Resume:Void()
        ResumeMusic()
        ResumeSounds()
    End

    Method ResumeMusic:Void()
        audio.ResumeMusic()
    End

    Method ResumeSounds:Void()
        For Local i := 0 To CHANNELS
            ResumeChannel(i)
        End
    End

    Private

    Method PlayMusic:Void(key:String)
        Local isLoop:Bool = loopEnabled.Contains(key)
        Local file:String = files.Get(key)

        For Local idx:Int = 0 Until extensions.Length()
            audio.PlayMusic(file + "." + extensions[idx], isLoop)
            If MusicState() = 1 Then Return
        End
    End

    Method PlaySound:Void(key:String)
        If Not sounds.Contains(key) Then Return

        Local channel:Int = GetFreeChannel()
        If channel = -1 Then Return

        audio.PlaySound(sounds.Get(key), channel, loopEnabled.Contains(key))
    End

    Method GetFreeChannel:Int()
        Local channelsChecked:Int = 0
        Local channelId:Int = lastChannel

        Repeat
            channelId += 1
            If channelId >= CHANNELS Then channelId = 0

            ' Skip the ChannelState check on Android, because:
            ' > Android: ChannelState always returns -1, ie: 'unknown'. Sounds
            ' >          to be used with PlaySound must be less than 1MB in
            ' >          length. Longer sounds can be played using the music
            ' >          commands.
            ' Source: http://blitz-wiki.appspot.com/mojo.audio
            If Target.IS_ANDROID Or ChannelState(channelId) = 0
                lastChannel = channelId
                Return channelId
            End

            channelsChecked += 1
        Until channelsChecked >= CHANNELS

        Return -1
    End

    Method LoadSound:Sound(file:String)
        Local tmpSound:Sound
        For Local idx:Int = 0 Until extensions.Length()
            tmpSound = audio.LoadSound(file + "." + extensions[idx])
            If Not (tmpSound = Null) Then Return tmpSound
        End

        If failOnLoadingError Then Error("Unable to load sound file: " + file)
        Return Null
    End
End


Fixed typo in format FlxU functions

Fixed typo in format FlxU functions

Strict

Import mojo
Import reflection

Import flxextern
Import flxpoint
Import flxg
Import system.flximagedata

Alias MonkeyAbs = monkey.math.Abs
Alias MonkeyFloor = monkey.math.Floor
Alias MonkeyCeil = monkey.math.Ceil
Alias MonkeyMin = monkey.math.Min
Alias MonkeyMax = monkey.math.Max
Alias MonkeyGetClass = reflection.GetClass

#If TARGET <> "psm" And TARGET <> "xna"
	Alias FlxOpenURL = mojo.app.OpenUrl
#End

Class FlxU

Private
	Global _objClass:ClassInfo

Public

	Function OpenURL:Void(url:String)
		FlxOpenURL(url)
	End Function

	Function Abs:Float(value:Float)
		Return MonkeyAbs(value)
	End Function

	Function Floor:Float(value:Float)
		Return MonkeyFloor(value)
	End Function

	Function Ceil:Float(value:Float)
		Return MonkeyCeil(value)
	End Function

	Function Round:Float(value:Float)
		If (value > 0) Then
			Return Int(value + .5)
		Else
			Return Int(value - .5)
		End If
	End Function

	Function Min:Float(number1:Float, number2:Float)
		Return MonkeyMin(number1, number2)
	End Function

	Function Max:Float(number1:Float, number2:Float)
		Return MonkeyMax(number1, number2)
	End Function

	Function Bound:Float(value:Float, min:Float, max:Float)
		Return Clamp(value, min, max)
	End Function

	Function Srand:Float(seed:Float)
		Return Float(seed Shr 8 & $ffffff)/$1000000
	End Function

	#Rem
	Function Shuffle:Object[](objects:Object[], howManyTimes:Int)
		Shuffle is not currently supported in Monkey. Use FlxArray.Shuffle method
	End Function
	#End

	#Rem
	Function GetRandom:FlxBasic(objects:FlxBasic[], startIndex:Int = 0, length:Int = 0)
		GetRandom is not currently supported in Monkey. Use FlxArray.GetRandom method
	End Function
	#End

	Function GetTicks:Int()
		Return Millisecs()
	End Function

	Function FormatTicks:String(startTicks:Int, endTicks:Int)
		Return ((endTicks - startTicks) / 1000) + "s"
	End Function

	Function MakeColor:Int(red:Int, green:Int, blue:Int, alpha:Float = 1)
		If (alpha <= 1) alpha *= 255
		Return (alpha & $FF) Shl 24 | (red & $FF) Shl 16 | (green & $FF) Shl 8 | (blue & $FF)
	End Function

	Function MakeColorFromHSB:Int(hue:Float, saturation:Float, brightness:Float, alpha:Float = 1)
		Local red:Float
		Local green:Float
		Local blue:Float

		If (saturation = 0)
			red = brightness
			green = brightness
			blue = brightness
		Else
			If (hue = 360) hue = 0

			Local slice:Int = hue / 60
			Local hf:Float = hue / 60 - slice
			Local aa:Float = brightness * (1 - saturation)
			Local bb:Float = brightness * (1 - saturation * hf)
			Local cc:Float = brightness * (1 - saturation * (1.0 - hf))

			Select (slice)
				Case 0
					red = brightness
					green = cc
					blue = aa

				Case 1
					red = bb
					green = brightness
					blue = aa

				Case 2
					red = aa
					green = brightness
					blue = cc

				Case 3
					red = aa
					green = bb
					blue = brightness

				Case 4
					red = cc
					green = aa
					blue = brightness

				Case 5
					red = brightness
					green = aa
					blue = bb

				Default
					red = 0
					green = 0
					blue = 0
			End Select
		End If

		If (alpha <= 1) alpha *= 255
		Return (alpha & $FF) Shl 24 | int(red * 255) Shl 16 | int(green * 255) Shl 8 | int(blue * 255)
	End Function

	Function GetRGBA:Float[](color:Int, resluts:Float[] = [])
		If (resluts.Length() <> 4) resluts = resluts.Resize(4)

		resluts[0] = (color Shr 16) & $FF
		resluts[1] = (color Shr 8) & $FF
		resluts[2] = color & $FF
		resluts[3] = Float((color Shr 24) & $FF) / 255
		Return resluts
	End Function

	Function GetHSB:Float[](color:Int, resluts:Float[] = [])
		If (resluts.Length() <> 4) resluts = resluts.Resize(4)

		Local red:Float = Float((color Shr 16) & $FF) / 255
		Local green:Float = Float((color Shr 8) & $FF) / 255
		Local blue:Float = Float(color & $FF) / 255

		Local m:Float = MonkeyMax(red, green)
		Local dmax:Float = MonkeyMax(m, blue)
		m = MonkeyMin(red, green)
		Local dmin:Float = MonkeyMin(m, blue)
		Local range:Float = dmax - dmin

		resluts[2] = dmax
		resluts[1] = 0
		resluts[0] = 0

		If (dmax <> 0) resluts[1] = range / dmax

		If (resluts[1] <> 0) Then
			If (red = dmax) Then
				resluts[0] = (green - blue) / range

			ElseIf (green = dmax) Then
				resluts[0] = 2 + (blue - red) / range

			ElseIf (blue = dmax) Then
				resluts[0] = 4 + (red - green) / range
			End If

			resluts[0] *= 60
			If (resluts[0] < 0) resluts[0] += 360
		End If

		resluts[3] = Float((color Shr 24) & $FF) / 255

		Return resluts
	End Function

	Function FormatTime:String(seconds:Float, showMS:Bool = False)
		Local timeString:StringStack = New StringStack()
		timeString.Push(Int(seconds / 60) + ":")

		Local timeStringHepler:Int = Int(seconds) Mod 60

		If (timeStringHepler < 10) Then
			timeString.Push("0")
		End If

		timeString.Push(timeStringHepler)

		If (showMS) Then
			timeString.Push(".")
			timeStringHepler = (seconds - Int(seconds)) * 100

			If (timeStringHepler < 10) Then
				timeString.Push("0")
			End If

			timeString.Push(timeStringHepler)
		End If

		Return timeString.Join("")
	End Function

	Function FormatMoney:String(amount:Float, showDecimal:Bool = True, englishStyle:Bool = True)
		Local helper:Int
		Local intAmount:Int = amount
		Local comma:String = ""
		Local result:String = ""
		Local zeroes:String = ""

		While (intAmount > 0)
			If (result.Length() > 0 And comma.Length() <= 0) Then
				If (englishStyle) Then
					comma = ","
				Else
					comma = "."
				End If
			End If

			helper = intAmount - Int(intAmount / 1000) * 1000
			intAmount /= 1000

			zeroes = ""

			If (intAmount > 0) Then
				If (helper < 100) Then
					zeroes = "0"
				End If

				If (helper < 10) Then
					zeroes = "00"
				End If
			End If

			result = zeroes + helper + comma + result
		Wend

		If (showDecimal) Then
			intAmount = Int(amount * 100) - (Int(amount) * 100)

			If (englishStyle) Then
				result += "." + intAmount
			Else
				result += "," + intAmount
			End If

			If (amount < 10) Then
				result += "0"
			End If
		End If

		Return result
	End Function

	Function GetClassName:String(obj:Object, simple:Bool = False)
		If (simple) Then
			Local name:String = MonkeyGetClass(obj).Name
			Return name[name.FindLast(".")+1..]
		End If

		Return MonkeyGetClass(obj).Name
	End Function

	Function CompareClassNames:Bool(object1:Object, object2:Object)
		Return (MonkeyGetClass(object1).Name = MonkeyGetClass(object2).Name)
	End Function

	Function GetClass:ClassInfo(name:String)
		Return MonkeyGetClass(name)
	End Function

	Function GetObjectClass:ClassInfo()
		If (_objClass <> Null)
			Return _objClass
		End If

		_objClass = MonkeyGetClass("monkey.lang.Object")

		Return _objClass
	End Function

	Function ComputeVelocity:Float(velocity:Float, acceleration:Float = 0, drag:Float = 0, max:Float = 10000)
		If (acceleration <> 0) Then
			velocity += acceleration * FlxG.Elapsed

		ElseIf (drag <> 0) Then
			drag *= FlxG.Elapsed

			If (velocity - drag > 0) Then
				velocity -= drag
			ElseIf (velocity + drag < 0) Then
				velocity += drag
			Else
				velocity = 0
			End If
		End If

		If (velocity <> 0 And max <> 10000) Then
			If (velocity > max) Then
				velocity = max
			ElseIf (velocity < -max) Then
				velocity = -max
			End If
		End If

		Return velocity
	End Function

	Function RotatePoint:FlxPoint(x:Float, y:Float, pivotX:Float, pivotY:Float, angle:Float, point:FlxPoint = Null)
		Local sin:Float = 0
		Local cos:Float = 0
		Local radians:Float = angle * -0.017453293

		While (radians < -PI)
			radians += TWOPI
		Wend

		While (radians > PI)
			radians = radians - TWOPI
		Wend

		If (radians < 0) Then
			sin =  1.27323954 * radians + .405284735 * radians * radians
			If (sin < 0) Then
				sin = .225 * (sin * -sin - sin) + sin
			Else
				sin = .225 * (sin * sin - sin) + sin
			End If
		Else
			sin = 1.27323954 * radians - 0.405284735 * radians * radians
			If (sin < 0) Then
				sin = .225 * (sin * -sin - sin) + sin
			Else
				sin = .225 * (sin * sin - sin) + sin
			End If
		End If

		radians += HALFPI
		If (radians > PI) radians -= TWOPI

		If (radians < 0) Then
			cos = 1.27323954 * radians + 0.405284735 * radians * radians
			If (cos < 0) Then
				cos = .225 * (cos * -cos - cos) + cos
			Else
				cos = .225 * (cos * cos - cos) + cos
			End If
		Else
			cos = 1.27323954 * radians - 0.405284735 * radians * radians
			If (cos < 0) Then
				cos = .225 * (cos * -cos - cos) + cos
			Else
				cos = .225 * (cos * cos - cos) + cos
			End If
		End If

		Local dx:Float = x - pivotX
		Local dy:Float = pivotY + y

		If (point = Null) point = New FlxPoint()
		point.x = pivotX + cos * dx - sin * dy
		point.y = pivotY - sin * dx - cos * dy

		Return point
	End Function

	Function GetAngle:Float(point1:FlxPoint, point2:FlxPoint)
		Local x:Float = point2.x - point1.x
		Local y:Float = point2.y - point1.y

		If (x = 0 And y = 0) Return 0

		Local c1:Float = PI * .25
		Local c2:Float = 3 * c1
		Local ay:Float = MonkeyAbs(y)
		Local angle:Float = 0

		If (x >= 0) Then
			angle = c1 - c1 * ((x - ay) / (x + ay))
		Else
			angle = c2 - c1 * ((x + ay) / (ay - x))
		End If

		If (y < 0) Then
			angle =	-angle * 57.2957796
		Else
			angle =	angle * 57.2957796
		End If

		If (angle > 90) Then
			angle = angle - 270
		Else
			angle += 90
		End If

		Return angle
	End Function

	Function GetDistance:Float(point1:FlxPoint, point2:FlxPoint)
		Local dx:Float = point1.x - point2.x
		Local dy:Float = point1.y - point2.y

		Return Sqrt(dx * dx + dy * dy)
	End Function

	Function GetDistance:Float(x1:Float, y1:Float, x2:Float, y2:Float)
		Local dx:Float = x1 - x2
		Local dy:Float = y1 - y2

		Return Sqrt(dx * dx + dy * dy)
	End Function

	Function SetImageMask:Image(image:Image, maskColor:Int)
		Local bitmapData:FlxImageData = FlxImageData.FromImage(image, True)
		bitmapData.ColorReplace(maskColor, 0)

		Local result:Image = bitmapData.Image
		bitmapData.Destroy()

		Return result
	End Function

	Function SetImagePadding:Image(image:Image, paddings:Int = Image.XYPadding)
		Local paddingX:Int, paddingY:Int

		If (paddings & Image.XPadding) paddingX = 1
		If (paddings & Image.YPadding) paddingY = 1

		Return FlxU.SetImagePadding(image, paddingX, paddingY)
	End Function

	Function SetImagePadding:Image(image:Image, paddingX:Int, paddingY:Int)
		Local bitmapData:FlxImageData = FlxImageData.FromImage(image, False, paddingX, paddingY)
		Local result:Image = bitmapData.Image
		bitmapData.Destroy()

		Return result
	End Function

End Class

Strict

Import mojo
Import reflection

Import flxextern
Import flxpoint
Import flxg
Import system.flximagedata

Alias MonkeyAbs = monkey.math.Abs
Alias MonkeyFloor = monkey.math.Floor
Alias MonkeyCeil = monkey.math.Ceil
Alias MonkeyMin = monkey.math.Min
Alias MonkeyMax = monkey.math.Max
Alias MonkeyGetClass = reflection.GetClass

#If TARGET <> "psm" And TARGET <> "xna"
	Alias FlxOpenURL = mojo.app.OpenUrl
#End

Class FlxU

Private
	Global _objClass:ClassInfo

Public

	Function OpenURL:Void(url:String)
		FlxOpenURL(url)
	End Function

	Function Abs:Float(value:Float)
		Return MonkeyAbs(value)
	End Function

	Function Floor:Float(value:Float)
		Return MonkeyFloor(value)
	End Function

	Function Ceil:Float(value:Float)
		Return MonkeyCeil(value)
	End Function

	Function Round:Float(value:Float)
		If (value > 0) Then
			Return Int(value + .5)
		Else
			Return Int(value - .5)
		End If
	End Function

	Function Min:Float(number1:Float, number2:Float)
		Return MonkeyMin(number1, number2)
	End Function

	Function Max:Float(number1:Float, number2:Float)
		Return MonkeyMax(number1, number2)
	End Function

	Function Bound:Float(value:Float, min:Float, max:Float)
		Return Clamp(value, min, max)
	End Function

	Function Srand:Float(seed:Float)
		Return Float(seed Shr 8 & $ffffff)/$1000000
	End Function

	#Rem
	Function Shuffle:Object[](objects:Object[], howManyTimes:Int)
		Shuffle is not currently supported in Monkey. Use FlxArray.Shuffle method
	End Function
	#End

	#Rem
	Function GetRandom:FlxBasic(objects:FlxBasic[], startIndex:Int = 0, length:Int = 0)
		GetRandom is not currently supported in Monkey. Use FlxArray.GetRandom method
	End Function
	#End

	Function GetTicks:Int()
		Return Millisecs()
	End Function

	Function FormatTicks:String(startTicks:Int, endTicks:Int)
		Return ((endTicks - startTicks) / 1000) + "s"
	End Function

	Function MakeColor:Int(red:Int, green:Int, blue:Int, alpha:Float = 1)
		If (alpha <= 1) alpha *= 255
		Return (alpha & $FF) Shl 24 | (red & $FF) Shl 16 | (green & $FF) Shl 8 | (blue & $FF)
	End Function

	Function MakeColorFromHSB:Int(hue:Float, saturation:Float, brightness:Float, alpha:Float = 1)
		Local red:Float
		Local green:Float
		Local blue:Float

		If (saturation = 0)
			red = brightness
			green = brightness
			blue = brightness
		Else
			If (hue = 360) hue = 0

			Local slice:Int = hue / 60
			Local hf:Float = hue / 60 - slice
			Local aa:Float = brightness * (1 - saturation)
			Local bb:Float = brightness * (1 - saturation * hf)
			Local cc:Float = brightness * (1 - saturation * (1.0 - hf))

			Select (slice)
				Case 0
					red = brightness
					green = cc
					blue = aa

				Case 1
					red = bb
					green = brightness
					blue = aa

				Case 2
					red = aa
					green = brightness
					blue = cc

				Case 3
					red = aa
					green = bb
					blue = brightness

				Case 4
					red = cc
					green = aa
					blue = brightness

				Case 5
					red = brightness
					green = aa
					blue = bb

				Default
					red = 0
					green = 0
					blue = 0
			End Select
		End If

		If (alpha <= 1) alpha *= 255
		Return (alpha & $FF) Shl 24 | int(red * 255) Shl 16 | int(green * 255) Shl 8 | int(blue * 255)
	End Function

	Function GetRGBA:Float[](color:Int, resluts:Float[] = [])
		If (resluts.Length() <> 4) resluts = resluts.Resize(4)

		resluts[0] = (color Shr 16) & $FF
		resluts[1] = (color Shr 8) & $FF
		resluts[2] = color & $FF
		resluts[3] = Float((color Shr 24) & $FF) / 255
		Return resluts
	End Function

	Function GetHSB:Float[](color:Int, resluts:Float[] = [])
		If (resluts.Length() <> 4) resluts = resluts.Resize(4)

		Local red:Float = Float((color Shr 16) & $FF) / 255
		Local green:Float = Float((color Shr 8) & $FF) / 255
		Local blue:Float = Float(color & $FF) / 255

		Local m:Float = MonkeyMax(red, green)
		Local dmax:Float = MonkeyMax(m, blue)
		m = MonkeyMin(red, green)
		Local dmin:Float = MonkeyMin(m, blue)
		Local range:Float = dmax - dmin

		resluts[2] = dmax
		resluts[1] = 0
		resluts[0] = 0

		If (dmax <> 0) resluts[1] = range / dmax

		If (resluts[1] <> 0) Then
			If (red = dmax) Then
				resluts[0] = (green - blue) / range

			ElseIf (green = dmax) Then
				resluts[0] = 2 + (blue - red) / range

			ElseIf (blue = dmax) Then
				resluts[0] = 4 + (red - green) / range
			End If

			resluts[0] *= 60
			If (resluts[0] < 0) resluts[0] += 360
		End If

		resluts[3] = Float((color Shr 24) & $FF) / 255

		Return resluts
	End Function

	Function FromatTime:String(seconds:Float, showMS:Bool = False)
		Local timeString:StringStack = New StringStack()
		timeString.Push(Int(seconds / 60) + ":")

		Local timeStringHepler:Int = Int(seconds) Mod 60

		If (timeStringHepler < 10) Then
			timeString.Push("0")
		End If

		timeString.Push(timeStringHepler)

		If (showMS) Then
			timeString.Push(".")
			timeStringHepler = (seconds - Int(seconds)) * 100

			If (timeStringHepler < 10) Then
				timeString.Push("0")
			End If

			timeString.Push(timeStringHepler)
		End If

		Return timeString.Join("")
	End Function

	Function FromatMoney:String(amount:Float, showDecimal:Bool = True, englishStyle:Bool = True)
		Local helper:Int
		Local intAmount:Int = amount
		Local comma:String = ""
		Local result:String = ""
		Local zeroes:String = ""

		While (intAmount > 0)
			If (result.Length() > 0 And comma.Length() <= 0) Then
				If (englishStyle) Then
					comma = ","
				Else
					comma = "."
				End If
			End If

			helper = intAmount - Int(intAmount / 1000) * 1000
			intAmount /= 1000

			zeroes = ""

			If (intAmount > 0) Then
				If (helper < 100) Then
					zeroes = "0"
				End If

				If (helper < 10) Then
					zeroes = "00"
				End If
			End If

			result = zeroes + helper + comma + result
		Wend

		If (showDecimal) Then
			intAmount = Int(amount * 100) - (Int(amount) * 100)

			If (englishStyle) Then
				result += "." + intAmount
			Else
				result += "," + intAmount
			End If

			If (amount < 10) Then
				result += "0"
			End If
		End If

		Return result
	End Function

	Function GetClassName:String(obj:Object, simple:Bool = False)
		If (simple) Then
			Local name:String = MonkeyGetClass(obj).Name
			Return name[name.FindLast(".")+1..]
		End If

		Return MonkeyGetClass(obj).Name
	End Function

	Function CompareClassNames:Bool(object1:Object, object2:Object)
		Return (MonkeyGetClass(object1).Name = MonkeyGetClass(object2).Name)
	End Function

	Function GetClass:ClassInfo(name:String)
		Return MonkeyGetClass(name)
	End Function

	Function GetObjectClass:ClassInfo()
		If (_objClass <> Null)
			Return _objClass
		End If

		_objClass = MonkeyGetClass("monkey.lang.Object")

		Return _objClass
	End Function

	Function ComputeVelocity:Float(velocity:Float, acceleration:Float = 0, drag:Float = 0, max:Float = 10000)
		If (acceleration <> 0) Then
			velocity += acceleration * FlxG.Elapsed

		ElseIf (drag <> 0) Then
			drag *= FlxG.Elapsed

			If (velocity - drag > 0) Then
				velocity -= drag
			ElseIf (velocity + drag < 0) Then
				velocity += drag
			Else
				velocity = 0
			End If
		End If

		If (velocity <> 0 And max <> 10000) Then
			If (velocity > max) Then
				velocity = max
			ElseIf (velocity < -max) Then
				velocity = -max
			End If
		End If

		Return velocity
	End Function

	Function RotatePoint:FlxPoint(x:Float, y:Float, pivotX:Float, pivotY:Float, angle:Float, point:FlxPoint = Null)
		Local sin:Float = 0
		Local cos:Float = 0
		Local radians:Float = angle * -0.017453293

		While (radians < -PI)
			radians += TWOPI
		Wend

		While (radians > PI)
			radians = radians - TWOPI
		Wend

		If (radians < 0) Then
			sin =  1.27323954 * radians + .405284735 * radians * radians
			If (sin < 0) Then
				sin = .225 * (sin * -sin - sin) + sin
			Else
				sin = .225 * (sin * sin - sin) + sin
			End If
		Else
			sin = 1.27323954 * radians - 0.405284735 * radians * radians
			If (sin < 0) Then
				sin = .225 * (sin * -sin - sin) + sin
			Else
				sin = .225 * (sin * sin - sin) + sin
			End If
		End If

		radians += HALFPI
		If (radians > PI) radians -= TWOPI

		If (radians < 0) Then
			cos = 1.27323954 * radians + 0.405284735 * radians * radians
			If (cos < 0) Then
				cos = .225 * (cos * -cos - cos) + cos
			Else
				cos = .225 * (cos * cos - cos) + cos
			End If
		Else
			cos = 1.27323954 * radians - 0.405284735 * radians * radians
			If (cos < 0) Then
				cos = .225 * (cos * -cos - cos) + cos
			Else
				cos = .225 * (cos * cos - cos) + cos
			End If
		End If

		Local dx:Float = x - pivotX
		Local dy:Float = pivotY + y

		If (point = Null) point = New FlxPoint()
		point.x = pivotX + cos * dx - sin * dy
		point.y = pivotY - sin * dx - cos * dy

		Return point
	End Function

	Function GetAngle:Float(point1:FlxPoint, point2:FlxPoint)
		Local x:Float = point2.x - point1.x
		Local y:Float = point2.y - point1.y

		If (x = 0 And y = 0) Return 0

		Local c1:Float = PI * .25
		Local c2:Float = 3 * c1
		Local ay:Float = MonkeyAbs(y)
		Local angle:Float = 0

		If (x >= 0) Then
			angle = c1 - c1 * ((x - ay) / (x + ay))
		Else
			angle = c2 - c1 * ((x + ay) / (ay - x))
		End If

		If (y < 0) Then
			angle =	-angle * 57.2957796
		Else
			angle =	angle * 57.2957796
		End If

		If (angle > 90) Then
			angle = angle - 270
		Else
			angle += 90
		End If

		Return angle
	End Function

	Function GetDistance:Float(point1:FlxPoint, point2:FlxPoint)
		Local dx:Float = point1.x - point2.x
		Local dy:Float = point1.y - point2.y

		Return Sqrt(dx * dx + dy * dy)
	End Function

	Function GetDistance:Float(x1:Float, y1:Float, x2:Float, y2:Float)
		Local dx:Float = x1 - x2
		Local dy:Float = y1 - y2

		Return Sqrt(dx * dx + dy * dy)
	End Function

	Function SetImageMask:Image(image:Image, maskColor:Int)
		Local bitmapData:FlxImageData = FlxImageData.FromImage(image, True)
		bitmapData.ColorReplace(maskColor, 0)

		Local result:Image = bitmapData.Image
		bitmapData.Destroy()

		Return result
	End Function

	Function SetImagePadding:Image(image:Image, paddings:Int = Image.XYPadding)
		Local paddingX:Int, paddingY:Int

		If (paddings & Image.XPadding) paddingX = 1
		If (paddings & Image.YPadding) paddingY = 1

		Return FlxU.SetImagePadding(image, paddingX, paddingY)
	End Function

	Function SetImagePadding:Image(image:Image, paddingX:Int, paddingY:Int)
		Local bitmapData:FlxImageData = FlxImageData.FromImage(image, False, paddingX, paddingY)
		Local result:Image = bitmapData.Image
		bitmapData.Destroy()

		Return result
	End Function

End Class