Typo fixed in FlxInvaders

Typo fixed in FlxInvaders

Strict

Import flixel

Import playership
Import alien

Class PlayState Extends FlxState Implements FlxQuadTreeOverlapNotifyCallback

	Global _CLASS:FlxClass = New PlayStateClass()

	Field player:PlayerShip
	Field playerBullets:FlxGroup
	Field aliens:FlxGroup
	Field alienBullets:FlxGroup

	Field vsPlayerBullets:FlxGroup
	Field vsAlienBullets:FlxGroup

	Method Create:Void()
		player = New PlayerShip()
		Add(player)

		Local numPlayerBullets:Int = 8
		playerBullets = New FlxGroup(numPlayerBullets)
		Local sprite:FlxSprite
		For Local i:Int = 0 Until numPlayerBullets
			sprite = New FlxSprite(-100, -100)
			sprite.MakeGraphic(2, 8)
			sprite.exists = False
			playerBullets .Add(sprite)
		Next
		Add(playerBullets)

		Local numAlienBullets:Int = 32
		alienBullets = New FlxGroup(numAlienBullets)

		For Local i:Int = 0 Until numAlienBullets
			sprite = New FlxSprite(-100, -100)
			sprite.MakeGraphic(2, 8)
			sprite.exists = False
			alienBullets.Add(sprite)
		Next
		Add(alienBullets)

		Local numAliens:Int = 50
		aliens = New FlxGroup(numAliens)
		Local a:Alien
		Local colors:Int[] = [FlxG.BLUE, FlxG.BLUE | FlxG.GREEN, FlxG.GREEN, FlxG.GREEN | FlxG.RED, FlxG.RED]

		For Local i:Int = 0 Until numAliens
			a = New Alien(8 + (i Mod 10) * 32, 24 + Int(i / 10) * 32, colors[Int(i / 10)], alienBullets)
			aliens.Add(a)
		Next
		Add(aliens)

		vsPlayerBullets = New FlxGroup()
		vsPlayerBullets.Add(aliens)

		vsAlienBullets = New FlxGroup()
		vsAlienBullets.Add(player)
	End Method

	Method Update:Void()
		If (KeyHit(KEY_ESCAPE)) Error ""

		Local alien:Alien = Alien(aliens.Members[0])
		If (alien.x < 0 Or alien.x > 16) Then
			aliens.CallAll(Alien.ReverseVelocity)
		End If

		FlxG.Overlap(playerBullets, vsPlayerBullets, Self)
		FlxG.Overlap(alienBullets, vsAlienBullets, Self)

		Super.Update()
	End Method

	Method OnOverlap:Void(object1:FlxObject,object2:FlxObject)
		object1.Kill()
		object2.Kill()
	End Method

End Class

Class PlayStateClass Implements FlxClass

	Method CreateInstance:FlxBasic()
		Return New PlayState()
	End Method

	Method InstanceOf:Bool(Object:FlxBasic)
		Return (PlayState(Object) <> Null)
	End Method

End Class

Strict

Import flixel

Import playership
Import alien

Class PlayState Extends FlxState Implements FlxQuadTreeOverlapNotifyCallback

	Global _CLASS:FlxClass = New PlayStateClass()

	Field player:PlayerShip
	Field playerBullets:FlxGroup
	Field aliens:FlxGroup
	Field aliensBullets:FlxGroup

	Field vsPlayerBullets:FlxGroup
	Field vsAlienBullets:FlxGroup

	Method Create:Void()
		player = New PlayerShip()
		Add(player)

		Local numPlayerBullets:Int = 8
		playerBullets = New FlxGroup(numPlayerBullets)
		Local sprite:FlxSprite
		For Local i:Int = 0 Until numPlayerBullets
			sprite = New FlxSprite(-100, -100)
			sprite.MakeGraphic(2, 8)
			sprite.exists = False
			playerBullets .Add(sprite)
		Next
		Add(playerBullets)

		Local numAlienBullets:Int = 32
		aliensBullets = New FlxGroup(numAlienBullets)

		For Local i:Int = 0 Until numAlienBullets
			sprite = New FlxSprite(-100, -100)
			sprite.MakeGraphic(2, 8)
			sprite.exists = False
			aliensBullets.Add(sprite)
		Next
		Add(aliensBullets)

		Local numAliens:Int = 50
		aliens = New FlxGroup(numAliens)
		Local a:Alien
		Local colors:Int[] = [FlxG.BLUE, FlxG.BLUE | FlxG.GREEN, FlxG.GREEN, FlxG.GREEN | FlxG.RED, FlxG.RED]

		For Local i:Int = 0 Until numAliens
			a = New Alien(8 + (i Mod 10) * 32, 24 + Int(i / 10) * 32, colors[Int(i / 10)], aliensBullets)
			aliens.Add(a)
		Next
		Add(aliens)

		vsPlayerBullets = New FlxGroup()
		vsPlayerBullets.Add(aliens)

		vsAlienBullets = New FlxGroup()
		vsAlienBullets.Add(player)
	End Method

	Method Update:Void()
		If (KeyHit(KEY_ESCAPE)) Error ""

		Local alien:Alien = Alien(aliens.Members[0])
		If (alien.x < 0 Or alien.x > 16) Then
			aliens.CallAll(Alien.ReverseVelocity)
		End If

		FlxG.Overlap(playerBullets, vsPlayerBullets, Self)
		FlxG.Overlap(alienBullets, vsAlienBullets, Self)

		Super.Update()
	End Method

	Method OnOverlap:Void(object1:FlxObject,object2:FlxObject)
		object1.Kill()
		object2.Kill()
	End Method

End Class

Class PlayStateClass Implements FlxClass

	Method CreateInstance:FlxBasic()
		Return New PlayState()
	End Method

	Method InstanceOf:Bool(Object:FlxBasic)
		Return (PlayState(Object) <> Null)
	End Method

End Class


BlitzMax target currently support only one joystick #51

Strict

Import mojo
Import reflection

Import flxextern
Import flxbasic
Import flxgame
Import flxcamera
Import flxobject
Import flxsound
Import flxmusic
Import flxu

Import system.input.accel
Import system.input.joystick
Import system.input.keyboard
Import system.input.mouse
Import system.input.touch
Import system.tweens.flxtween
Import system.tweens.util.ease
Import system.tweens.misc.multivartween
Import system.resolutionpolicy.flxresolutionpolicy
Import system.resolutionpolicy.fill
Import system.flxresourcesmanager
Import system.flxquadtree
Import system.flxreplay

Import plugin.timermanager
Import plugin.debugpathdisplay

Alias AccelInput = accel.Accel
Alias MouseInput = mouse.Mouse
Alias TouchInput = touch.Touch
Alias JoystickInput = joystick.Joystick
Alias MojoDeviceWidth = mojo.graphics.DeviceWidth
Alias MojoDeviceHeight = mojo.graphics.DeviceHeight

Class FlxG

	Const LIBRARY_NAME:String = "Flixel for Monkey"

	Const LIBRARY_MAJOR_VERSION:Int = 1

	Const LIBRARY_MINOR_VERSION:Int = 0

	Const DATA_SUFFIX:String = "_flx"

	Const RED:Int = $FFFF0012

	Const GREEN:Int = $FF00F225

	Const BLUE:Int = $FF0090E9

	Const PINK:Int = $FFF01EFF

	Const WHITE:Int = $FFFFFFFF

	Const BLACK:Int = $FF000000

	Global Paused:Bool

#If CONFIG = "debug"
	Global Debug:Bool = True
#Else
	Global Debug:Bool = False
#End

	Global Elapsed:Float

	Global TimeScale:Float

	Global DeviceWidth:Int

	Global DeviceHeight:Int

	Global Width:Int

	Global Height:Int

	Global WorldBounds:FlxRect

	Global WorldDivisions:Int

	Global VisualDebug:Bool

	Global Mobile:Bool

	Global GlobalSeed:Int

	Global Scores:Stack<Int>

	Global Score:Int

	Global Levels:Stack<Int>

	Global Level:Int

	Global Accel:AccelInput

	Global Keys:Keyboard

	Global Mouse:MouseInput

	Global Music:FlxMusic

	Global Sounds:FlxGroup

	Global Mute:Bool

	Global Cameras:Stack<FlxCamera>

	Global Camera:FlxCamera

	Global Plugins:Stack<FlxBasic>

	Global VolumeHandler:FlxVolumeChangeListener

	Global Framerate:Int

	Global Updaterate:Int

	Global Tweener:FlxBasic

	Global _DeviceScaleFactorX:Float = 1

	Global _DeviceScaleFactorY:Float = 1

	Global _DeviceOffsetX:Int

	Global _DeviceOffsetY:Int

	Global _BgColor:FlxColor = FlxColor.ARGB(FlxG.BLACK)

	Global _Game:FlxGame

	Global _Volume:Float

	Global _BitmapCache:FlxResourcesManager<Image>

	Global _SoundCache:FlxResourcesManager<Sound>

	Global _LastDrawingColor:Int

	Global _LastDrawingAlpha:Float

	Global _LastDrawingBlend:Int

	Global _CurrentCamera:FlxCamera

Private
#If TARGET = "psm" Or TARGET = "bmax"
	Const _JOY_UNITS_COUNT:Int = 1
#Else
	Const _JOY_UNITS_COUNT:Int = 4
#End
	Const _TOUCH_COUNT:Int = 32

	Global _Joystick:JoystickInput[_JOY_UNITS_COUNT]

	Global _Touch:TouchInput[_TOUCH_COUNT]

	Global _CollideListener:FlxCollideProcessListener = New FlxCollideProcessListener()

	Global _ResolutionPolicy:FlxResolutionPolicy = New FillResolutionPolicy()

	Global _Point:FlxPoint = New FlxPoint()

Public
	Function GetLibraryName:String()
		Return FlxG.LIBRARY_NAME + " v" + FlxG.LIBRARY_MAJOR_VERSION + "." + FlxG.LIBRARY_MINOR_VERSION
	End Function

	Function Log:Void(data:String)
		Print data
		'TODO
	End Function

	Function Random:Float()
		FlxG.GlobalSeed = (FlxG.GlobalSeed * 1664525 + 1013904223)|0
		Return FlxU.Srand(FlxG.GlobalSeed)
	End Function

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

	Function LoadReplay:Void(data:String, state:FlxState = Null, cancelKeys:Int[] = [], timeout:Float = 0, callback:FlxReplayListener = Null)
		_Game._replay.Load(data)
		If (state = Null) Then
			FlxG.ResetGame()
		Else
			FlxG.SwitchState(state)
		End If

		_Game._replayCancelKeys = cancelKeys
		_Game._replayTimer = timeout * 1000
		_Game._replayCallback = callback
		_Game._replayRequested = True
	End Function

	Function ReloadReplay:Void(standardMode:Bool = True)
		If (standardMode) Then
			FlxG.ResetGame()
		Else
			FlxG.ResetState()
		End If

		If (_Game._replay.frameCount > 0) _Game._replayRequested = True
	End Function

	Function StopReplay:Void()
		_Game._replaying = False

		If (_Game._debugger <> Null) Then
			'TODO
		End If

		ResetInput()
	End Function

	Function RecordReplay:Void(standardMode:Bool = True)
		If (standardMode) Then
			FlxG.ResetGame()
		Else
			FlxG.ResetState()
		End If

		_Game._recordingRequested = True
	End Function

	Function StopRecording:String()
		_Game._recording = False

		If (_Game._debugger <> Null) Then
			'TODO
		End if

		Return _Game._replay.Save()
	End Function

	Function ResetState:Void()
		_Game._requestedState = FlxState(_Game._state.GetClass().NewInstance())
	End Function

	Function ResetGame:Void()
		_Game._requestedReset = True
	End Function

	Function ResetInput:Void()
		For Local i:Int = 0 Until _JOY_UNITS_COUNT
			_Joystick[i].Reset()
		Next

		For Local i:Int = 0 Until _TOUCH_COUNT
			_Touch[i].Reset()
		Next

		Accel.Reset()
		Mouse.Reset()

	#If TARGET <> "android" And TARGET <> "psm"
		Keys.Reset()
	#Else
		Keys.Reset(KEY_BACKSPACE, KEY_QUOTES)
	#End
	End Function

	Function PlayMusic:Void(music:String, volume:Float = 1.0)
		If (FlxG.Music = Null) Then
			FlxG.Music = New FlxMusic()

		ElseIf (FlxG.Music.active) Then
			FlxG.Music.Stop()
		End If

		FlxG.Music.Load(music, True)
		FlxG.Music.Volume = volume
		FlxG.Music.survive = True

		FlxG.Music.Play()
	End Function

	Function LoadSound:FlxSound(sound:String, volume:Float = 1.0, looped:Bool = False, autoDestroy:Bool = False, autoPlay:Bool = False, stopPrevious:Bool = True)
		Local s:FlxSound = FlxSound(Sounds.Recycle(ClassInfo(FlxSound.ClassObject)))

		s.Load(sound, looped, autoDestroy, stopPrevious)
		s.Volume = volume
		If (autoPlay) s.Play()

		Return s
	End Function

	Function Play:FlxSound(sound:String, volume:Float = 1.0, looped:Bool = False, autoDestroy:Bool = True)
		Local s:FlxSound = FlxG.LoadSound(sound, volume, looped, autoDestroy, True, False)
		If (Not looped) s.exists = False
		Return s
	End Function

	Function Volume:Float()
		Return _Volume
	End Function

	Function Volume:Void(volume:Float)
		_Volume = volume

		If (_Volume < 0) Then
			_Volume = 0
		ElseIf (_Volume > 1) Then
			_Volume = 1
		End If

		If (VolumeHandler <> Null) Then
			If (FlxG.Mute) Then
				VolumeHandler.OnVolumeChange(0)
			Else
				VolumeHandler.OnVolumeChange(_Volume)
			End If
		End If

		If (Music <> Null And Music.active) Then
			Music._UpdateTransform()
		End If

		For Local basic:FlxBasic = EachIn Sounds
			FlxSound(basic)._UpdateTransform()
		Next
	End Function

	Function DestroySounds:Void(forceDestroy:Bool = False)
		If (Music <> Null And (forceDestroy Or Not Music.survive)) Then
			Music.Destroy()
			Music = Null
		End If

		Local sound:FlxSound

		For Local basic:FlxBasic = EachIn Sounds
			sound = FlxSound(basic)

			If (sound <> Null And (forceDestroy Or Not sound.survive)) Then
				sound.Destroy()
			End If
		Next
	End Function

	Function UpdateSounds:Void()
		If (Music <> Null And Music.active) Then
			Music.Update()
		End If

		If (Sounds <> Null And Sounds.active) Then
			Sounds.Update()
		End If
	End Function

	Function PauseSounds:Void()
		If (Music <> Null And Music.exists And Music.active) Then
			Music.Pause()
		End If

		Local sound:FlxSound

		For Local basic:FlxBasic = EachIn Sounds
			sound = FlxSound(basic)

			If (sound <> Null And sound.exists And sound.active) Then
				sound.Pause()
			End If
		Next
	End Function

	Function ResumeSounds:Void()
		If (Music <> Null And Music.exists) Then
			Music.Play()
		End If

		Local sound:FlxSound

		For Local basic:FlxBasic = EachIn Sounds
			sound = FlxSound(basic)

			If (sound <> Null And sound.exists) Then
				sound.Resume()
			End If
		Next
	End Function

	Function CheckBitmapCache:Bool(key:String)
		Return _BitmapCache.CheckResource(key)
	End Function

	Function AddBitmap:Image(graphic:String, graphicLoader:FlxResourceLoader<Image>, unique:Bool = False, key:String = "")
		If (key.Length() = 0) Then
			key = graphic

			If (unique And CheckBitmapCache(key)) Then
				Local inc:Int = 0
				Local ukey:String

				Repeat
					ukey = key + inc
					inc += 1
				Until(CheckBitmapCache(ukey))

				key = ukey
			End If
		End If

		Return _BitmapCache.GetResource(key, graphicLoader)
	End Function

	Function RemoveBitmap:Void(graphic:String)
		If (_BitmapCache <> Null) Then
			Local image:Image = _BitmapCache.Resources.Get(graphic)
			If (image <> Null) Then
				image.Discard()
				_BitmapCache.RemoveResource(graphic)
			End If
		End If
	End Function

	Function ClearBitmapCache:Void()
		If (_BitmapCache = Null) _BitmapCache = New FlxResourcesManager<Image>()

		For Local image:Image = EachIn _BitmapCache.Resources.Values()
			If (image <> Null) image.Discard()
		Next

		_BitmapCache.Clear()
	End Function

	Function AddSound:Sound(sound:String, soundLoader:FlxResourceLoader<Sound>)
		Return _SoundCache.GetResource(sound, soundLoader)
	End Function

	Function ClearSoundCache:Void()
		If (_SoundCache = Null) _SoundCache = New FlxResourcesManager<Sound>()

		For Local sound:Sound = EachIn _SoundCache.Resources.Values()
			If (sound <> Null) sound.Discard()
		Next

		_SoundCache.Clear()
	End Function

	Function State:FlxState()
		Return _Game._state
	End Function

	Function SwitchState:Void(state:FlxState)
		FlxG._Game._requestedState = state
	End Function

	Function AddCamera:FlxCamera(newCamera:FlxCamera)
		FlxG.Cameras.Push(newCamera)
		Return newCamera
	End Function

	Function RemoveCamera:Void(camera:FlxCamera, destroy:Bool = True)
		FlxG.Cameras.RemoveEach(camera)
		If (destroy) camera.Destroy()
	End Function

	Function ResetCameras:Void(newCamera:FlxCamera = Null)
		Local cam:FlxCamera
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			cam = FlxG.Cameras.Get(i)
			cam.Destroy()
			i+=1
		Wend

		FlxG.Cameras.Clear()
		FlxG.Camera = Null

		If (newCamera = Null) newCamera = New FlxCamera(0, 0, FlxG.Width, FlxG.Height)

		FlxG.Camera = FlxG.AddCamera(newCamera)
	End Function

	Function Flash:Void(color:Int = FlxG.WHITE, duration:Float = 1, onComplete:FlxCameraFlashListener = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).Flash(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Fade:Void(color:Int = FlxG.BLACK, duration:Float = 1, onComplete:FlxCameraFadeListener = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).Fade(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Shake:Void(intensity:Float = 0.05, duration:Float = 0.5, onComplete:FlxCameraShakeListener = Null, force:Bool = True, direction:Int = FlxCamera.SHAKE_BOTH_AXES)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).Shake(intensity, duration, onComplete, force, direction)
			i+=1
		Wend
	End Function

	Function BgColor:Int()
		If (FlxG.Camera = Null Or Not FlxG.Camera.alive) Return FlxG._BgColor.argb
		Return FlxG.Camera.BgColor
	End Function

	Function SetBgColor:Void(color:Int)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).BgColor = color
			i+=1
		Wend

		FlxG._BgColor.SetARGB(color)
	End Function

	Function Overlap:Bool(objectOrGroup1:FlxBasic = Null, objectOrGroup2:FlxBasic, notifyCallback:FlxOverlapNotifyListener = Null, processCallback:FlxOverlapProcessListener = Null)
		If (objectOrGroup1 = Null) objectOrGroup1 = FlxG.State()
		If (objectOrGroup2 = objectOrGroup1) objectOrGroup2 = Null

		FlxQuadTree.Divisions = FlxG.WorldDivisions

		Local quadTree:FlxQuadTree = FlxQuadTree.Recycle(FlxG.WorldBounds.x, FlxG.WorldBounds.y, FlxG.WorldBounds.width, FlxG.WorldBounds.height)
		quadTree.Load(objectOrGroup1, objectOrGroup2, notifyCallback, processCallback)

		Local result:Bool = quadTree.Execute()
		quadTree.Destroy()

		Return result
	End Function

	Function Collide:Bool(objectOrGroup1:FlxBasic = Null, objectOrGroup2:FlxBasic = Null, notifyCallback:FlxOverlapNotifyListener = Null)
		Return	Overlap(objectOrGroup1, objectOrGroup2, notifyCallback, _CollideListener)
	End Function

	Function AddPlugin:FlxBasic(plugin:FlxBasic)
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			If (pluginList.Get(i).ToString() = plugin.ToString()) Then
				Return plugin
			End If

			i+=1
		Wend

		pluginList.Push(plugin)
		Return plugin
	End Function

	Function GetPlugin:FlxBasic(objectClass:ClassInfo)
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local plugin:FlxBasic
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.GetClass().ExtendsClass(objectClass)) Return plugin

			i+=1
		Wend

		Return Null
	End Function

	Function RemovePlugin:FlxBasic(plugin:FlxBasic)
		Plugins.RemoveEach(plugin)
		Return plugin
	End Function

	Function RemovePluginType:Bool(objectClass:ClassInfo)
		Local results:Bool = False
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = pluginList.Length() - 1

		While(i >= 0)
			If (pluginList.Get(i).GetClass().ExtendsClass(objectClass)) Then
				pluginList.Remove(i)
				results = True
			End If

			i-=1
		Wend

		Return results
	End Function

	Function Init:Void(game:FlxGame, width:Int, height:Int, zoom:Float)
		FlxG._Game = game
		FlxG.Width = width
		FlxG.Height = height

		FlxG.Tweener = New FlxBasic()

		FlxG.Mute = False
		FlxG._Volume = .5
		FlxG.Sounds = New FlxGroup()
		FlxG.VolumeHandler = Null

		FlxG.ClearBitmapCache()

		FlxCamera.DefaultZoom = zoom
		FlxG.Cameras = New Stack<FlxCamera>()

		Plugins = New Stack<FlxBasic>
		AddPlugin(New DebugPathDisplay())
		AddPlugin(New TimerManager())

		FlxG.Accel = New AccelInput()
		FlxG.Keys = New Keyboard()
		FlxG.Mouse = New MouseInput()

		For Local i:Int = 0 Until _JOY_UNITS_COUNT
			_Joystick[i] = New JoystickInput(i)
		Next

		For Local i:Int = 0 Until _TOUCH_COUNT
			_Touch[i] = New TouchInput(i)
		Next

		FlxG.Mobile = IsMobile()

		FlxG.Scores = New Stack<Int>()
		FlxG.Levels = New Stack<Int>()
		FlxG.VisualDebug = False
	End Function

	Function Reset:Void()
		FlxG.ClearBitmapCache()
		FlxG.DestroySounds(True)
		FlxG.ClearSoundCache()
		FlxG.ResetInput()
		FlxG.Levels.Clear()
		FlxG.Scores.Clear()
		FlxG.Level = 0
		FlxG.Score = 0
		FlxG.TimeScale = 1
		FlxG.Elapsed = 0
		FlxG.GlobalSeed = Rnd(1, 10000000)
		FlxG.WorldBounds = New FlxRect(-10, -10, FlxG.Width + 20, FlxG.Height + 20)
		FlxG.WorldDivisions = 6
		Local debugPathDisplay:DebugPathDisplay = DebugPathDisplay(FlxG.GetPlugin(ClassInfo(DebugPathDisplay.ClassObject)))
		If (debugPathDisplay <> Null) debugPathDisplay.Clear()
	End Function

	Function UpdateInput:Void()
	#If TARGET = "html5" Or TARGET = "ios" Or TARGET = "android" Or TARGET = "psm"
		Accel.Update(AccelX(), AccelY(), AccelZ())
	#End

	#If TARGET = "glfw" Or TARGET = "psm" Or TARGET = "bmax"
		For Local i:Int = 0 Until _JOY_UNITS_COUNT
			_Joystick[i].Update()
		Next
	#End

	#If TARGET = "xna"
		If (Not FlxG.Mobile) Then
			For Local i:Int = 0 Until _JOY_UNITS_COUNT
				_Joystick[i].Update()
			Next
		Else
			Accel.Update(AccelX(), AccelY(), AccelZ())
		End If
	#End

	#If TARGET = "ios" Or TARGET = "android" Or TARGET = "psm"
		For Local i:Int = 0 Until _TOUCH_COUNT
			_Touch[i].Update(TouchX(i), TouchY(i))

			If (i > 0 And Not _Touch[i].Used) Exit
		Next

	#ElseIf TARGET = "html5" Or TARGET = "flash"
		If (Not FlxG.Mobile) Then
			Keys.Update()
		End If

		_Touch[0].Update(TouchX(), TouchY())

	#ElseIf TARGET = "xna"
		If (Not FlxG.Mobile) Then
			Keys.Update()
			_Touch[0].Update(TouchX(), TouchY())
		Else
			For Local i:Int = 0 Until _TOUCH_COUNT
				_Touch[i].Update(TouchX(i), TouchY(i))

				If (i > 0 And Not _Touch[i].Used) Exit
			Next
		End If

	#Else
		Keys.Update()
		_Touch[0].Update(TouchX(), TouchY())
	#End

	#If TARGET = "android" Or TARGET = "psm"
		Keys.Update()
	#End

		If (Not _Game._debuggerUp Or Not _Game._debugger.hasMouse) Then
			Mouse.Update(MouseX(), MouseY())
		End If
	End Function

	Function UpdateCameras:Void()
		Local cam:FlxCamera
		Local cams:Stack<FlxCamera> = FlxG.Cameras
		Local i:Int = 0
		Local l:Int = cams.Length()

		While(i < l)
			cam = cams.Get(i)
			If (cam <> Null And cam.exists And cam.active) cam.Update()
			i+=1
		Wend
	End Function

	Function UpdatePlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.active) plugin.Update()
			i+=1
		Wend
	End Function

	Function DrawPlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.visible) plugin.Draw()
			i+=1
		Wend
	End Function

	Function Joystick:JoystickInput(unit:Int = 0)
		Return _Joystick[unit]
	End Function

	Function Touch:TouchInput(index:Int = 0)
		Return _Touch[index]
	End Function

	Function JoystickCount:Int()
		Return _JOY_UNITS_COUNT
	End Function

	Function TouchCount:Int()
		Return _TOUCH_COUNT
	End Function

	Function SetResolutionPolicy:Void(resolutionPolicy:FlxResolutionPolicy)
		_ResolutionPolicy = resolutionPolicy
		_Measure()
	End Function

	Function UpdateDevice:Void()
		If(FlxG.DeviceWidth <> MojoDeviceWidth() Or FlxG.DeviceHeight <> MojoDeviceHeight()) Then
			_Measure()
		End If
	End Function

	Function Tween:MultiVarTween(object:Object, values:StringMap<Float>, duration:Float, type:Int = FlxTween.ONESHOT, complete:FlxTweenListener = Null, ease:FlxEaseFunction = Null, tweener:FlxBasic = Null)
		If (tweener = Null) Then
			If (FlxBasic(object) <> Null) Then
				tweener = FlxBasic(object)
			Else
				tweener = FlxG.Tweener
			End If
		End If

		Local tween:MultiVarTween = New MultiVarTween(complete, type)
		tween.Tween(object, values, duration, ease)
		tweener.AddTween(tween)

		Return tween
	End Function

Private
	Function _Measure:Void()
		FlxG.DeviceWidth = MojoDeviceWidth()
		FlxG.DeviceHeight = MojoDeviceHeight()

		_ResolutionPolicy.OnMeasure(FlxG.DeviceWidth, FlxG.DeviceHeight, _Point)

		FlxG._DeviceScaleFactorX = _Point.x / Float(FlxG.Width)
		FlxG._DeviceScaleFactorY = _Point.y / Float(FlxG.Height)

		Local zoom:Float = FlxCamera.DefaultZoom
		If(FlxG.Camera <> Null) zoom = FlxG.Camera.Zoom

		FlxG._DeviceOffsetX = Ceil( (FlxG.DeviceWidth - _Point.x * zoom) * 0.5)
		FlxG._DeviceOffsetY = Ceil( (FlxG.DeviceHeight - _Point.y * zoom) * 0.5)

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()
		Local cam:FlxCamera

		While(i < l)
			cam = FlxG.Cameras.Get(i)

			'recalc values
			cam.X = cam.X
			cam.Y = cam.Y
			cam.Width = cam.Width
			cam.Height = cam.Height

			i += 1
		Wend
	End Function

End Class

Private
Class FlxCollideProcessListener Implements FlxOverlapProcessListener

	Method OnOverlapProcess:Bool(object1:FlxObject, object2:FlxObject)
		Return FlxObject.Separate(object1, object2)
	End Method

End Class

Strict

Import mojo
Import reflection

Import flxextern
Import flxbasic
Import flxgame
Import flxcamera
Import flxobject
Import flxsound
Import flxmusic
Import flxu

Import system.input.accel
Import system.input.joystick
Import system.input.keyboard
Import system.input.mouse
Import system.input.touch
Import system.tweens.flxtween
Import system.tweens.util.ease
Import system.tweens.misc.multivartween
Import system.resolutionpolicy.flxresolutionpolicy
Import system.resolutionpolicy.fill
Import system.flxresourcesmanager
Import system.flxquadtree
Import system.flxreplay

Import plugin.timermanager
Import plugin.debugpathdisplay

Alias AccelInput = accel.Accel
Alias MouseInput = mouse.Mouse
Alias TouchInput = touch.Touch
Alias JoystickInput = joystick.Joystick
Alias MojoDeviceWidth = mojo.graphics.DeviceWidth
Alias MojoDeviceHeight = mojo.graphics.DeviceHeight

Class FlxG

	Const LIBRARY_NAME:String = "Flixel for Monkey"

	Const LIBRARY_MAJOR_VERSION:Int = 1

	Const LIBRARY_MINOR_VERSION:Int = 0

	Const DATA_SUFFIX:String = "_flx"

	Const RED:Int = $FFFF0012

	Const GREEN:Int = $FF00F225

	Const BLUE:Int = $FF0090E9

	Const PINK:Int = $FFF01EFF

	Const WHITE:Int = $FFFFFFFF

	Const BLACK:Int = $FF000000

	Global Paused:Bool

#If CONFIG = "debug"
	Global Debug:Bool = True
#Else
	Global Debug:Bool = False
#End

	Global Elapsed:Float

	Global TimeScale:Float

	Global DeviceWidth:Int

	Global DeviceHeight:Int

	Global Width:Int

	Global Height:Int

	Global WorldBounds:FlxRect

	Global WorldDivisions:Int

	Global VisualDebug:Bool

	Global Mobile:Bool

	Global GlobalSeed:Int

	Global Scores:Stack<Int>

	Global Score:Int

	Global Levels:Stack<Int>

	Global Level:Int

	Global Accel:AccelInput

	Global Keys:Keyboard

	Global Mouse:MouseInput

	Global Music:FlxMusic

	Global Sounds:FlxGroup

	Global Mute:Bool

	Global Cameras:Stack<FlxCamera>

	Global Camera:FlxCamera

	Global Plugins:Stack<FlxBasic>

	Global VolumeHandler:FlxVolumeChangeListener

	Global Framerate:Int

	Global Updaterate:Int

	Global Tweener:FlxBasic

	Global _DeviceScaleFactorX:Float = 1

	Global _DeviceScaleFactorY:Float = 1

	Global _DeviceOffsetX:Int

	Global _DeviceOffsetY:Int

	Global _BgColor:FlxColor = FlxColor.ARGB(FlxG.BLACK)

	Global _Game:FlxGame

	Global _Volume:Float

	Global _BitmapCache:FlxResourcesManager<Image>

	Global _SoundCache:FlxResourcesManager<Sound>

	Global _LastDrawingColor:Int

	Global _LastDrawingAlpha:Float

	Global _LastDrawingBlend:Int

	Global _CurrentCamera:FlxCamera

Private
#If TARGET = "psm"
	Const _JOY_UNITS_COUNT:Int = 1
#Else
	Const _JOY_UNITS_COUNT:Int = 4
#End
	Const _TOUCH_COUNT:Int = 32

	Global _Joystick:JoystickInput[_JOY_UNITS_COUNT]

	Global _Touch:TouchInput[_TOUCH_COUNT]

	Global _CollideListener:FlxCollideProcessListener = New FlxCollideProcessListener()

	Global _ResolutionPolicy:FlxResolutionPolicy = New FillResolutionPolicy()

	Global _Point:FlxPoint = New FlxPoint()

Public
	Function GetLibraryName:String()
		Return FlxG.LIBRARY_NAME + " v" + FlxG.LIBRARY_MAJOR_VERSION + "." + FlxG.LIBRARY_MINOR_VERSION
	End Function

	Function Log:Void(data:String)
		Print data
		'TODO
	End Function

	Function Random:Float()
		FlxG.GlobalSeed = (FlxG.GlobalSeed * 1664525 + 1013904223)|0
		Return FlxU.Srand(FlxG.GlobalSeed)
	End Function

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

	Function LoadReplay:Void(data:String, state:FlxState = Null, cancelKeys:Int[] = [], timeout:Float = 0, callback:FlxReplayListener = Null)
		_Game._replay.Load(data)
		If (state = Null) Then
			FlxG.ResetGame()
		Else
			FlxG.SwitchState(state)
		End If

		_Game._replayCancelKeys = cancelKeys
		_Game._replayTimer = timeout * 1000
		_Game._replayCallback = callback
		_Game._replayRequested = True
	End Function

	Function ReloadReplay:Void(standardMode:Bool = True)
		If (standardMode) Then
			FlxG.ResetGame()
		Else
			FlxG.ResetState()
		End If

		If (_Game._replay.frameCount > 0) _Game._replayRequested = True
	End Function

	Function StopReplay:Void()
		_Game._replaying = False

		If (_Game._debugger <> Null) Then
			'TODO
		End If

		ResetInput()
	End Function

	Function RecordReplay:Void(standardMode:Bool = True)
		If (standardMode) Then
			FlxG.ResetGame()
		Else
			FlxG.ResetState()
		End If

		_Game._recordingRequested = True
	End Function

	Function StopRecording:String()
		_Game._recording = False

		If (_Game._debugger <> Null) Then
			'TODO
		End if

		Return _Game._replay.Save()
	End Function

	Function ResetState:Void()
		_Game._requestedState = FlxState(_Game._state.GetClass().NewInstance())
	End Function

	Function ResetGame:Void()
		_Game._requestedReset = True
	End Function

	Function ResetInput:Void()
		For Local i:Int = 0 Until _JOY_UNITS_COUNT
			_Joystick[i].Reset()
		Next

		For Local i:Int = 0 Until _TOUCH_COUNT
			_Touch[i].Reset()
		Next

		Accel.Reset()
		Mouse.Reset()

	#If TARGET <> "android" And TARGET <> "psm"
		Keys.Reset()
	#Else
		Keys.Reset(KEY_BACKSPACE, KEY_QUOTES)
	#End
	End Function

	Function PlayMusic:Void(music:String, volume:Float = 1.0)
		If (FlxG.Music = Null) Then
			FlxG.Music = New FlxMusic()

		ElseIf (FlxG.Music.active) Then
			FlxG.Music.Stop()
		End If

		FlxG.Music.Load(music, True)
		FlxG.Music.Volume = volume
		FlxG.Music.survive = True

		FlxG.Music.Play()
	End Function

	Function LoadSound:FlxSound(sound:String, volume:Float = 1.0, looped:Bool = False, autoDestroy:Bool = False, autoPlay:Bool = False, stopPrevious:Bool = True)
		Local s:FlxSound = FlxSound(Sounds.Recycle(ClassInfo(FlxSound.ClassObject)))

		s.Load(sound, looped, autoDestroy, stopPrevious)
		s.Volume = volume
		If (autoPlay) s.Play()

		Return s
	End Function

	Function Play:FlxSound(sound:String, volume:Float = 1.0, looped:Bool = False, autoDestroy:Bool = True)
		Local s:FlxSound = FlxG.LoadSound(sound, volume, looped, autoDestroy, True, False)
		If (Not looped) s.exists = False
		Return s
	End Function

	Function Volume:Float()
		Return _Volume
	End Function

	Function Volume:Void(volume:Float)
		_Volume = volume

		If (_Volume < 0) Then
			_Volume = 0
		ElseIf (_Volume > 1) Then
			_Volume = 1
		End If

		If (VolumeHandler <> Null) Then
			If (FlxG.Mute) Then
				VolumeHandler.OnVolumeChange(0)
			Else
				VolumeHandler.OnVolumeChange(_Volume)
			End If
		End If

		If (Music <> Null And Music.active) Then
			Music._UpdateTransform()
		End If

		For Local basic:FlxBasic = EachIn Sounds
			FlxSound(basic)._UpdateTransform()
		Next
	End Function

	Function DestroySounds:Void(forceDestroy:Bool = False)
		If (Music <> Null And (forceDestroy Or Not Music.survive)) Then
			Music.Destroy()
			Music = Null
		End If

		Local sound:FlxSound

		For Local basic:FlxBasic = EachIn Sounds
			sound = FlxSound(basic)

			If (sound <> Null And (forceDestroy Or Not sound.survive)) Then
				sound.Destroy()
			End If
		Next
	End Function

	Function UpdateSounds:Void()
		If (Music <> Null And Music.active) Then
			Music.Update()
		End If

		If (Sounds <> Null And Sounds.active) Then
			Sounds.Update()
		End If
	End Function

	Function PauseSounds:Void()
		If (Music <> Null And Music.exists And Music.active) Then
			Music.Pause()
		End If

		Local sound:FlxSound

		For Local basic:FlxBasic = EachIn Sounds
			sound = FlxSound(basic)

			If (sound <> Null And sound.exists And sound.active) Then
				sound.Pause()
			End If
		Next
	End Function

	Function ResumeSounds:Void()
		If (Music <> Null And Music.exists) Then
			Music.Play()
		End If

		Local sound:FlxSound

		For Local basic:FlxBasic = EachIn Sounds
			sound = FlxSound(basic)

			If (sound <> Null And sound.exists) Then
				sound.Resume()
			End If
		Next
	End Function

	Function CheckBitmapCache:Bool(key:String)
		Return _BitmapCache.CheckResource(key)
	End Function

	Function AddBitmap:Image(graphic:String, graphicLoader:FlxResourceLoader<Image>, unique:Bool = False, key:String = "")
		If (key.Length() = 0) Then
			key = graphic

			If (unique And CheckBitmapCache(key)) Then
				Local inc:Int = 0
				Local ukey:String

				Repeat
					ukey = key + inc
					inc += 1
				Until(CheckBitmapCache(ukey))

				key = ukey
			End If
		End If

		Return _BitmapCache.GetResource(key, graphicLoader)
	End Function

	Function RemoveBitmap:Void(graphic:String)
		If (_BitmapCache <> Null) Then
			Local image:Image = _BitmapCache.Resources.Get(graphic)
			If (image <> Null) Then
				image.Discard()
				_BitmapCache.RemoveResource(graphic)
			End If
		End If
	End Function

	Function ClearBitmapCache:Void()
		If (_BitmapCache = Null) _BitmapCache = New FlxResourcesManager<Image>()

		For Local image:Image = EachIn _BitmapCache.Resources.Values()
			If (image <> Null) image.Discard()
		Next

		_BitmapCache.Clear()
	End Function

	Function AddSound:Sound(sound:String, soundLoader:FlxResourceLoader<Sound>)
		Return _SoundCache.GetResource(sound, soundLoader)
	End Function

	Function ClearSoundCache:Void()
		If (_SoundCache = Null) _SoundCache = New FlxResourcesManager<Sound>()

		For Local sound:Sound = EachIn _SoundCache.Resources.Values()
			If (sound <> Null) sound.Discard()
		Next

		_SoundCache.Clear()
	End Function

	Function State:FlxState()
		Return _Game._state
	End Function

	Function SwitchState:Void(state:FlxState)
		FlxG._Game._requestedState = state
	End Function

	Function AddCamera:FlxCamera(newCamera:FlxCamera)
		FlxG.Cameras.Push(newCamera)
		Return newCamera
	End Function

	Function RemoveCamera:Void(camera:FlxCamera, destroy:Bool = True)
		FlxG.Cameras.RemoveEach(camera)
		If (destroy) camera.Destroy()
	End Function

	Function ResetCameras:Void(newCamera:FlxCamera = Null)
		Local cam:FlxCamera
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			cam = FlxG.Cameras.Get(i)
			cam.Destroy()
			i+=1
		Wend

		FlxG.Cameras.Clear()
		FlxG.Camera = Null

		If (newCamera = Null) newCamera = New FlxCamera(0, 0, FlxG.Width, FlxG.Height)

		FlxG.Camera = FlxG.AddCamera(newCamera)
	End Function

	Function Flash:Void(color:Int = FlxG.WHITE, duration:Float = 1, onComplete:FlxCameraFlashListener = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).Flash(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Fade:Void(color:Int = FlxG.BLACK, duration:Float = 1, onComplete:FlxCameraFadeListener = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).Fade(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Shake:Void(intensity:Float = 0.05, duration:Float = 0.5, onComplete:FlxCameraShakeListener = Null, force:Bool = True, direction:Int = FlxCamera.SHAKE_BOTH_AXES)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).Shake(intensity, duration, onComplete, force, direction)
			i+=1
		Wend
	End Function

	Function BgColor:Int()
		If (FlxG.Camera = Null Or Not FlxG.Camera.alive) Return FlxG._BgColor.argb
		Return FlxG.Camera.BgColor
	End Function

	Function SetBgColor:Void(color:Int)
		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG.Cameras.Get(i).BgColor = color
			i+=1
		Wend

		FlxG._BgColor.SetARGB(color)
	End Function

	Function Overlap:Bool(objectOrGroup1:FlxBasic = Null, objectOrGroup2:FlxBasic, notifyCallback:FlxOverlapNotifyListener = Null, processCallback:FlxOverlapProcessListener = Null)
		If (objectOrGroup1 = Null) objectOrGroup1 = FlxG.State()
		If (objectOrGroup2 = objectOrGroup1) objectOrGroup2 = Null

		FlxQuadTree.Divisions = FlxG.WorldDivisions

		Local quadTree:FlxQuadTree = FlxQuadTree.Recycle(FlxG.WorldBounds.x, FlxG.WorldBounds.y, FlxG.WorldBounds.width, FlxG.WorldBounds.height)
		quadTree.Load(objectOrGroup1, objectOrGroup2, notifyCallback, processCallback)

		Local result:Bool = quadTree.Execute()
		quadTree.Destroy()

		Return result
	End Function

	Function Collide:Bool(objectOrGroup1:FlxBasic = Null, objectOrGroup2:FlxBasic = Null, notifyCallback:FlxOverlapNotifyListener = Null)
		Return	Overlap(objectOrGroup1, objectOrGroup2, notifyCallback, _CollideListener)
	End Function

	Function AddPlugin:FlxBasic(plugin:FlxBasic)
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			If (pluginList.Get(i).ToString() = plugin.ToString()) Then
				Return plugin
			End If

			i+=1
		Wend

		pluginList.Push(plugin)
		Return plugin
	End Function

	Function GetPlugin:FlxBasic(objectClass:ClassInfo)
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local plugin:FlxBasic
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.GetClass().ExtendsClass(objectClass)) Return plugin

			i+=1
		Wend

		Return Null
	End Function

	Function RemovePlugin:FlxBasic(plugin:FlxBasic)
		Plugins.RemoveEach(plugin)
		Return plugin
	End Function

	Function RemovePluginType:Bool(objectClass:ClassInfo)
		Local results:Bool = False
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = pluginList.Length() - 1

		While(i >= 0)
			If (pluginList.Get(i).GetClass().ExtendsClass(objectClass)) Then
				pluginList.Remove(i)
				results = True
			End If

			i-=1
		Wend

		Return results
	End Function

	Function Init:Void(game:FlxGame, width:Int, height:Int, zoom:Float)
		FlxG._Game = game
		FlxG.Width = width
		FlxG.Height = height

		FlxG.Tweener = New FlxBasic()

		FlxG.Mute = False
		FlxG._Volume = .5
		FlxG.Sounds = New FlxGroup()
		FlxG.VolumeHandler = Null

		FlxG.ClearBitmapCache()

		FlxCamera.DefaultZoom = zoom
		FlxG.Cameras = New Stack<FlxCamera>()

		Plugins = New Stack<FlxBasic>
		AddPlugin(New DebugPathDisplay())
		AddPlugin(New TimerManager())

		FlxG.Accel = New AccelInput()
		FlxG.Keys = New Keyboard()
		FlxG.Mouse = New MouseInput()

		For Local i:Int = 0 Until _JOY_UNITS_COUNT
			_Joystick[i] = New JoystickInput(i)
		Next

		For Local i:Int = 0 Until _TOUCH_COUNT
			_Touch[i] = New TouchInput(i)
		Next

		FlxG.Mobile = IsMobile()

		FlxG.Scores = New Stack<Int>()
		FlxG.Levels = New Stack<Int>()
		FlxG.VisualDebug = False
	End Function

	Function Reset:Void()
		FlxG.ClearBitmapCache()
		FlxG.DestroySounds(True)
		FlxG.ClearSoundCache()
		FlxG.ResetInput()
		FlxG.Levels.Clear()
		FlxG.Scores.Clear()
		FlxG.Level = 0
		FlxG.Score = 0
		FlxG.TimeScale = 1
		FlxG.Elapsed = 0
		FlxG.GlobalSeed = Rnd(1, 10000000)
		FlxG.WorldBounds = New FlxRect(-10, -10, FlxG.Width + 20, FlxG.Height + 20)
		FlxG.WorldDivisions = 6
		Local debugPathDisplay:DebugPathDisplay = DebugPathDisplay(FlxG.GetPlugin(ClassInfo(DebugPathDisplay.ClassObject)))
		If (debugPathDisplay <> Null) debugPathDisplay.Clear()
	End Function

	Function UpdateInput:Void()
	#If TARGET = "html5" Or TARGET = "ios" Or TARGET = "android" Or TARGET = "psm"
		Accel.Update(AccelX(), AccelY(), AccelZ())
	#End

	#If TARGET = "glfw" Or TARGET = "psm" Or TARGET = "bmax"
		For Local i:Int = 0 Until _JOY_UNITS_COUNT
			_Joystick[i].Update()
		Next
	#End

	#If TARGET = "xna"
		If (Not FlxG.Mobile) Then
			For Local i:Int = 0 Until _JOY_UNITS_COUNT
				_Joystick[i].Update()
			Next
		Else
			Accel.Update(AccelX(), AccelY(), AccelZ())
		End If
	#End

	#If TARGET = "ios" Or TARGET = "android" Or TARGET = "psm"
		For Local i:Int = 0 Until _TOUCH_COUNT
			_Touch[i].Update(TouchX(i), TouchY(i))

			If (i > 0 And Not _Touch[i].Used) Exit
		Next

	#ElseIf TARGET = "html5" Or TARGET = "flash"
		If (Not FlxG.Mobile) Then
			Keys.Update()
		End If

		_Touch[0].Update(TouchX(), TouchY())

	#ElseIf TARGET = "xna"
		If (Not FlxG.Mobile) Then
			Keys.Update()
			_Touch[0].Update(TouchX(), TouchY())
		Else
			For Local i:Int = 0 Until _TOUCH_COUNT
				_Touch[i].Update(TouchX(i), TouchY(i))

				If (i > 0 And Not _Touch[i].Used) Exit
			Next
		End If

	#Else
		Keys.Update()
		_Touch[0].Update(TouchX(), TouchY())
	#End

	#If TARGET = "android" Or TARGET = "psm"
		Keys.Update()
	#End

		If (Not _Game._debuggerUp Or Not _Game._debugger.hasMouse) Then
			Mouse.Update(MouseX(), MouseY())
		End If
	End Function

	Function UpdateCameras:Void()
		Local cam:FlxCamera
		Local cams:Stack<FlxCamera> = FlxG.Cameras
		Local i:Int = 0
		Local l:Int = cams.Length()

		While(i < l)
			cam = cams.Get(i)
			If (cam <> Null And cam.exists And cam.active) cam.Update()
			i+=1
		Wend
	End Function

	Function UpdatePlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.active) plugin.Update()
			i+=1
		Wend
	End Function

	Function DrawPlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.Plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.visible) plugin.Draw()
			i+=1
		Wend
	End Function

	Function Joystick:JoystickInput(unit:Int = 0)
		Return _Joystick[unit]
	End Function

	Function Touch:TouchInput(index:Int = 0)
		Return _Touch[index]
	End Function

	Function JoystickCount:Int()
		Return _JOY_UNITS_COUNT
	End Function

	Function TouchCount:Int()
		Return _TOUCH_COUNT
	End Function

	Function SetResolutionPolicy:Void(resolutionPolicy:FlxResolutionPolicy)
		_ResolutionPolicy = resolutionPolicy
		_Measure()
	End Function

	Function UpdateDevice:Void()
		If(FlxG.DeviceWidth <> MojoDeviceWidth() Or FlxG.DeviceHeight <> MojoDeviceHeight()) Then
			_Measure()
		End If
	End Function

	Function Tween:MultiVarTween(object:Object, values:StringMap<Float>, duration:Float, type:Int = FlxTween.ONESHOT, complete:FlxTweenListener = Null, ease:FlxEaseFunction = Null, tweener:FlxBasic = Null)
		If (tweener = Null) Then
			If (FlxBasic(object) <> Null) Then
				tweener = FlxBasic(object)
			Else
				tweener = FlxG.Tweener
			End If
		End If

		Local tween:MultiVarTween = New MultiVarTween(complete, type)
		tween.Tween(object, values, duration, ease)
		tweener.AddTween(tween)

		Return tween
	End Function

Private
	Function _Measure:Void()
		FlxG.DeviceWidth = MojoDeviceWidth()
		FlxG.DeviceHeight = MojoDeviceHeight()

		_ResolutionPolicy.OnMeasure(FlxG.DeviceWidth, FlxG.DeviceHeight, _Point)

		FlxG._DeviceScaleFactorX = _Point.x / Float(FlxG.Width)
		FlxG._DeviceScaleFactorY = _Point.y / Float(FlxG.Height)

		Local zoom:Float = FlxCamera.DefaultZoom
		If(FlxG.Camera <> Null) zoom = FlxG.Camera.Zoom

		FlxG._DeviceOffsetX = Ceil( (FlxG.DeviceWidth - _Point.x * zoom) * 0.5)
		FlxG._DeviceOffsetY = Ceil( (FlxG.DeviceHeight - _Point.y * zoom) * 0.5)

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()
		Local cam:FlxCamera

		While(i < l)
			cam = FlxG.Cameras.Get(i)

			'recalc values
			cam.X = cam.X
			cam.Y = cam.Y
			cam.Width = cam.Width
			cam.Height = cam.Height

			i += 1
		Wend
	End Function

End Class

Private
Class FlxCollideProcessListener Implements FlxOverlapProcessListener

	Method OnOverlapProcess:Bool(object1:FlxObject, object2:FlxObject)
		Return FlxObject.Separate(object1, object2)
	End Method

End Class


Added a description for the 'save_and_load' demo.

Strict

Public

#Rem
	DESCRIPTION:
		This is an 'os' unit test, meant to showcase saving and
		loading a file, using the active version of 'os'.

		Code control-flow may or may not be self-contained,
		but overall behavior should be consistent across boundaries.
#End

' Preprocessor related:
#If LANG = "js"
	#VIRTUALOS_DEMO_SAVEANDLOAD_JS_EXTS = True
#End

' Imports:

' This is imported for maximum compatibility.
Import regal.virtualos.os

' External bindings:
Extern

#If VIRTUALOS_DEMO_SAVEANDLOAD_JS_EXTS
	Function Confirm:Bool(Message:String)="confirm"
	Function Alert:Void(Message:String)="alert"
#End

Public

' Functions:
Function Main:Int()
	' Constant variable(s):
	Const MessagePath:= "Thing.txt"

	' Local variable(s):
	Local Message:= "This is a test."

	Print("Loading a message from ~q" + MessagePath + "~q...")

	Local LoadedVersion:= LoadString(MessagePath)

	If (LoadedVersion.Length = 0) Then
		Print("Loading failed; creating file entry.")

		Print("Saving '" + Message + "' to ~q" + MessagePath + "~q...")

		Local SaveResponse:= SaveString(Message, MessagePath)

		Print("Response from 'SaveString': " + SaveResponse)

		If (SaveResponse = 0) Then
			Print("Done.")

			Alert("File entry created.")
		Else
			Print("Unable to create file entry.")
		Endif
	Else
		Print("Done; resulting message:")
		Print("'" + Message + "'")

		If (Confirm("Would you like to remove the entry?")) Then
			Print("Removing entry from file-system...")

			If (DeleteFile(MessagePath)) Then
				Print("File ~q" + MessagePath + "~q has been deleted.")
			Else
				Print("Failed to delete ~q" + MessagePath + "~q; internal error.")
			Endif
		Endif
	Endif

	Print("Operations finished, exiting.")

	' Return the default response.
	Return 0
End

#If Not VIRTUALOS_DEMO_SAVEANDLOAD_JS_EXTS
	Function Confirm:Bool(Message:String)
		Return True ' False
	End

	Function Alert:Void(Message:String)
		Print("{ALERT}: " + Message)

		Return
	End
#End

Strict

Public

' Preprocessor related:
#If LANG = "js"
	#VIRTUALOS_DEMO_SAVEANDLOAD_JS_EXTS = True
#End

' Imports:
Import regal.virtualos.os

' External bindings:
Extern

#If VIRTUALOS_DEMO_SAVEANDLOAD_JS_EXTS
	Function Confirm:Bool(Message:String)="confirm"
	Function Alert:Void(Message:String)="alert"
#End

Public

' Functions:
Function Main:Int()
	' Constant variable(s):
	Const MessagePath:= "Thing.txt"

	' Local variable(s):
	Local Message:= "This is a test."

	Print("Loading a message from ~q" + MessagePath + "~q...")

	Local LoadedVersion:= LoadString(MessagePath)

	If (LoadedVersion.Length = 0) Then
		Print("Loading failed; creating file entry.")

		Print("Saving '" + Message + "' to ~q" + MessagePath + "~q...")

		Local SaveResponse:= SaveString(Message, MessagePath)

		Print("Response from 'SaveString': " + SaveResponse)

		If (SaveResponse = 0) Then
			Print("Done.")

			Alert("File entry created.")
		Else
			Print("Unable to create file entry.")
		Endif
	Else
		Print("Done; resulting message:")
		Print("'" + Message + "'")

		If (Confirm("Would you like to remove the entry?")) Then
			Print("Removing entry from file-system...")

			If (DeleteFile(MessagePath)) Then
				Print("File ~q" + MessagePath + "~q has been deleted.")
			Else
				Print("Failed to delete ~q" + MessagePath + "~q; internal error.")
			Endif
		Endif
	Endif

	Print("Operations finished, exiting.")

	' Return the default response.
	Return 0
End

#If Not VIRTUALOS_DEMO_SAVEANDLOAD_JS_EXTS
	Function Confirm:Bool(Message:String)
		Return True ' False
	End

	Function Alert:Void(Message:String)
		Print("{ALERT}: " + Message)

		Return
	End
#End


Dave spammed my email account with different versions of the filesystem - I think this is the correct/working version ;)

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

Strict
Import mojo

Class FileSystem Extends DataConversion
'Private
	Field delimiter:String
	Field _header:String = "MKYDATA"
	Field fileData:String
	Field index:StringMap<FileStream>
'Public

	Function Create:FileSystem(delimiter:String = String.FromChar(9)) 'tab-delimited by default
		Local t:FileSystem = New FileSystem
		t.delimiter = delimiter
		t.LoadAll()
		Return t
	End

	Method WriteFile:FileStream(filename:String)
		Local f:FileStream = new FileStream
		f.filename = filename.ToLower()
		f.ptr = 0
		f.delimiter = Self.delimiter
		Self.index.Insert(f.filename.ToLower(),f)
		Return f
	End

	Method ReadFile:FileStream(filename:String)
		filename = filename.ToLower()
		Local f:FileStream
		f = Self.index.ValueForKey(filename)
		f.ptr = 0
		f.arr = f.data.Split(f.delimiter)
		Return f
	End

	Method FileExists:Bool(filename:String)
		filename = filename.ToLower()
		if Self.index.Contains(filename)
			Return True
		Else
			Return False
		End
		Return False
	End

	Method ListDir:Void()
		Local filename:String
		Local stream:FileStream
		Print "Directory Listing:"
		For filename = EachIn Self.index.Keys()
			stream = Self.index.ValueForKey(filename)
			Print filename + "   " + stream.data.Length()+" byte(s)."
		Next
	End

	Method DeleteFile:Void(filename:String)
		filename = filename.ToLower()
		if Self.index.Contains(filename)
			Self.index.Remove(filename)
		End
	End

	Method SaveAll:Void()
		Local f:FileStream
		Self.fileData = Self._header'header
		Self.fileData+= Self.delimiter
		self.fileData+= Self.IntToString(Self.index.Count())'number of files in index
		if Self.index.Count() > 0
			For f = EachIn Self.index.Values()
				'store filename
				Self.fileData+=Self.delimiter
				Self.fileData+= f.filename
				'store data
				Self.fileData+=Self.delimiter
				Self.fileData+= Self.IntToString(f.numElements)
				if f.numElements > 0
					Self.fileData+=Self.delimiter
					Self.fileData+=f.data
				End
			Next
		End
		SaveState(Self.fileData)
	End

	Method LoadAll:Void()
		Local numFiles:Int
		Local numElements:Int
		Local stream:FileStream
		Local len:Int
		Local ptr:Int
		Local elementCounter:Int
		Local arr:String[]
		Self.fileData = LoadState()
		self.index = New StringMap<FileStream>
		if Self.fileData.Length() > 0
			arr = Self.fileData.Split(Self.delimiter)
			if arr[ptr] = Self._header
				Self.index.Clear()
				ptr+=1
				numFiles = Self.StringToInt(arr[ptr])
				ptr+=1
				if numFiles > 0
					For Local n:Int = 1 to numFiles
						stream = New FileStream
						'filename
						stream.delimiter = Self.delimiter
						stream.filename = arr[ptr] ; ptr+=1
						'data
						stream.numElements = Int(arr[ptr]) ; ptr+=1
						if stream.numElements > 0
							elementCounter = 0
							Repeat
								stream.data+=arr[ptr]
								elementCounter+=1
								if elementCounter < stream.numElements
									stream.data+=Self.delimiter
								EndIf
								ptr+=1
							Until elementCounter = stream.numElements
						End
						Self.index.Insert(stream.filename,stream)
					Next
				End
			End
		Else
			SaveState("")'save empty file and indicate no files stored
		End
	End
End

Class FileStream Extends DataConversion
	Field filename:String
	Field ptr:Int
'Private
	Field data:String
	Field arr:String[]
	Field delimiter:String
	Field numElements:Int
'Public

	Method ReadInt:Int()
		Local result:Int
		if Self.ptr >= Self.arr.Length()
			Self.EofError()
		EndIf

		result = Int(Self.arr[Self.ptr])
		Self.ptr+=1
		Return result
	End

	Method WriteInt:Void(val:Int)
		If Self.data.Length() > 0
			Self.data+=Self.delimiter
		EndIf
		Self.data+=Self.IntToString(val)
		Self.numElements+=1
	End

	Method ReadString:String()
		Local result:String
		if Self.ptr >= Self.arr.Length()
			Self.EofError()
		EndIf
		result = Self.arr[Self.ptr]
		Self.ptr+=1
		Return result
	End

	Method WriteString:Void(val:String)
		If Self.data.Length() > 0
			Self.data+=Self.delimiter
		EndIf
		Self.data+=val
		Self.numElements+=1
	End

	Method ReadFloat:Float()
		Local result:float
		if Self.ptr >= Self.arr.Length()
			Self.EofError()
		EndIf

		result = Float(Self.arr[Self.ptr])
		Self.ptr+=1
		Return result
	End

	Method WriteFloat:Void(val:Float)
		Local s:String = self.FloatToString(val)
		If Self.data.Length() > 0
			Self.data+=Self.delimiter
		EndIf
		Self.data+=s
		Self.numElements+=1
	End

	Method ReadBool:Bool()
		Local result:Bool
		if Self.ptr >= Self.arr.Length()
			Self.EofError()
		EndIf

		if Self.arr[Self.ptr] = "0"
			result = False
		Else
			result = True
		EndIf

		Self.ptr+=1
		Return result
	End Method

	Method WriteBool:Void(val:Bool)
		Local txt:String
		If Self.data.Length() > 0
			Self.data+=Self.delimiter
		EndIf
		If val
			txt = "1"
		Else
			txt = "0"
		EndIf
		Self.data+=txt
		Self.numElements+=1
	End Method

	Method EofError:Void()
		Error "End of file: " + Self.filename
	End Method
End

Class DataConversion
	Method LittleEndianIntToString:String(val:Int)
		Local result:String
		result = String.FromChar((val) & $FF)
		result+= String.FromChar((val Shr 8) & $FF)
		result+= String.FromChar((val Shr 16) & $FF)
		result+= String.FromChar((val Shr 24) & $FF)
		Return result
	End

	Method StringToLittleEndianInt:Int(val:String)
		Local result:Int
		result = (val[0])
		result|= (val[1] Shl 8)
		result|= (val[2] Shl 16)
		result|= (val[3] Shl 24)
		Return result
	End

    Method IntToString:String(val:Int)
		Return String(val)
    End

    Method StringToInt:Int(val:String)
		Return Int(val)
    End

	Method FloatToString:String(val:Float)
		Return String(val)
	End

	Method StringToFloat:Float(val:String)
		Return Float(val)
	End
End

Strict
Import mojo

Class FileSystem Extends DataConversion
Private
	Field _header:String = "MKYDATA"
	Field fileData:String
	Field index:StringMap<FileStream>
Public

	Method New()
		Self.LoadAll()
	End

	Method WriteFile:FileStream(filename:String)
		Local f:FileStream = new FileStream
		f.filename = filename.ToLower()
		f.fileptr = 0
		Self.index.Insert(f.filename.ToLower(),f)
		Return f
	End

	Method ReadFile:FileStream(filename:String)
		filename = filename.ToLower()
		Local f:FileStream
		f = Self.index.ValueForKey(filename)
		f.fileptr = 0
		Return f
	End

	Method FileExists:Bool(filename:String)
		filename = filename.ToLower()
		if Self.index.Contains(filename)
			Return True
		Else
			Return False
		End
		Return False
	End

	Method ListDir:Void()
		Local filename:String
		Local stream:FileStream
		Print "Directory Listing:"
		For filename = EachIn Self.index.Keys()
			stream = Self.index.ValueForKey(filename)
			Print filename + "   " + stream.data.Length()+" byte(s)."
		Next
	End

	Method DeleteFile:Void(filename:String)
		filename = filename.ToLower()
		if Self.index.Contains(filename)
			Self.index.Remove(filename)
		End
	End

	Method SaveAll:Void()
		Local f:FileStream
		Self.fileData = Self._header'header
		self.fileData+= Self.IntToString(Self.index.Count())'number of files in index
		if Self.index.Count() > 0
			For f = EachIn Self.index.Values()
				'store filename
				Self.fileData+= Self.IntToString(f.filename.Length())
				if f.filename.Length() > 0
					Self.fileData+= f.filename
				End
				'store data
				Self.fileData+= Self.IntToString(f.data.Length())
				if f.data.Length() > 0
					Self.fileData+= f.data
				End
			Next
		End
		SaveState(Self.fileData)
	End

	Method LoadAll:Void()
		Local numFiles:Int
		Local stream:FileStream
		Local len:Int
		Local ptr:Int
		Self.fileData = LoadState()
		self.index = New StringMap<FileStream>
		if Self.fileData.Length() > 0
			if Self.fileData.StartsWith(Self._header)
				Self.index.Clear()
				ptr+=Self._header.Length()
				numFiles = Self.StringToInt(Self.fileData[ptr..ptr+3])
				ptr+=3
				if numFiles > 0
					For Local n:Int = 1 to numFiles
						stream = New FileStream
						'filename
						len = Self.StringToInt(Self.fileData[ptr..ptr+3])
						ptr+=3
						if len > 0
							stream.filename = Self.fileData[ptr..ptr+len]
							ptr+=len
						End
						'data
						len = Self.StringToInt(Self.fileData[ptr..ptr+3])
						ptr+=3
						if len > 0
							stream.data = Self.fileData[ptr..ptr+len]
							ptr+=len
						End
						Self.index.Insert(stream.filename,stream)
					Next
				End
			End
		Else
			SaveState("")'save empty file and indicate no files stored
		End
	End
End

Class FileStream Extends DataConversion
	Field filename:String
	Field fileptr:Int
Private
	Field data:String
Public

	Method ReadInt:Int()
		Local result:string
		result = Self.data[Self.fileptr..self.fileptr+3]
		Self.fileptr+=3
		Return Self.StringToInt(result)
	End

	Method WriteInt:Void(val:Int)
		Self.data+=Self.IntToString(val)
	End

	Method ReadString:String()
		Local result:String
		Local strLen:Int = self.StringToInt(self.data[self.fileptr..self.fileptr+3])
		Self.fileptr+=3
		if strLen > 0
			result = Self.data[Self.fileptr..self.fileptr+strLen]
			Self.fileptr+=strLen
		End
		Return result
	End

	Method WriteString:Void(val:String)
		Self.data+=Self.IntToString(val.Length())
		if val.Length() > 0
			Self.data+=val
		End
	End

	Method ReadFloat:Float()
		Local result:float
		Local s:String
		Local strLen:Int = self.StringToInt(self.data[self.fileptr..self.fileptr+3])
		Self.fileptr+=3
		s = Self.data[Self.fileptr..self.fileptr+strLen]
		result = Self.StringToFloat(s)
		Self.fileptr+=strLen
		Return result
	End

	Method WriteFloat:Void(val:Float)
		Local s:String = self.FloatToString(val)
		Self.data+=Self.IntToString(s.Length())
		Self.data+=s
	End

	Method ReadBool:Bool()
		Local result:Bool
		result = Bool(Self.data[Self.fileptr])
		Self.fileptr+=1
		Return result
	End Method

	Method WriteBool:Void(val:Bool)
		Self.data+=String.FromChar(val)
	End Method
End

Class DataConversion
	Method LittleEndianIntToString:String(val:Int)
		Local result:String
		result = String.FromChar((val) & $FF)
		result+= String.FromChar((val Shr 8) & $FF)
		result+= String.FromChar((val Shr 16) & $FF)
		result+= String.FromChar((val Shr 24) & $FF)
		Return result
	End

	Method StringToLittleEndianInt:Int(val:String)
		Local result:Int
		result = (val[0])
		result|= (val[1] Shl 8)
		result|= (val[2] Shl 16)
		result|= (val[3] Shl 24)
		Return result
	End

	Method IntToString:String(val:Int)
		Local result:String
		result = String.FromChar($F000 | ((val Shr 20) & $0FFF) )
		result += String.FromChar($F000 | ((val Shr 8) & $0FFF))
		result += String.FromChar($F000 | (val & $00FF))
		Return result
	End

	Method StringToInt:Int(val:String)
		Return ((val[0]&$0FFF) Shl 20) | ((val[1]&$0FFF) Shl 8) |(val[2]&$00FF)
	End

	Method FloatToString:String(val:Float)
		Return String(val)
	End

	Method StringToFloat:Float(val:String)
		Return Float(val)
	End
End


Renamed Tween function to TweenCalc to fix a naming clash between the Tween Class and this function.

#Rem
Copyright (c) 2011 Steve Revill and Shane Woolcock
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#End

Function QuadTween:Float(b:Float, c:Float, t:Float, d:Float = 1)
	Local diff:Float = c - b
	t /= d / 2
	If t < 1 Return diff / 2 * t * t + b
	t -= 1
	Return -diff / 2 * (t * (t - 2) - 1) + b
End

Function QuinticTween:Float(b:Float, c:Float, t:Float, d:Float = 1)
	Local diff:Float = c - b
	t /= d / 2
	If (t < 1) Return diff / 2 * t * t * t * t * t + b
	t -= 2
	Return diff / 2 * (t * t * t * t * t + 2) + b
End

Function TweenCalc:Float(p1:Float, p2:Float, t:Float)
	Return p1 + t * (p2 - p1)
End

Function TweenSmooth:Float(p1:Float, p2:Float, t:Float)
	Local v:Float = SmoothStep(t)
	Return p1 + v * (p2 - p1)
End

Function SmoothStep:Float(x:Float, interpSmooth:Int = 1)
	For Local i:Int = 0 Until interpSmooth
		x *= x * (3 - 2 * x)
	Next

	Return x' x*x * (3-2*x)
End

Function TweenUp:Float(p1:Float, p2:Float, t:Float)
	Local v:Float = SmoothStep(t)
	v = Pow(v, 2) 'power of 2.
	Return p1 + v * (p2 - p1)
End

Function TweenDown:Float(p1:Float, p2:Float, t:Float)
	Local v:Float = SmoothStep(t)
	v = 1 - Pow(1 - v, 2) 'InvSquared
	Return p1 + v * (p2 - p1)
End

#Rem
Copyright (c) 2011 Steve Revill and Shane Woolcock
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#End

Function QuadTween:Float(b:Float, c:Float, t:Float, d:Float = 1)
	Local diff:Float = c - b
	t /= d / 2
	If t < 1 Return diff / 2 * t * t + b
	t -= 1
	Return -diff / 2 * (t * (t - 2) - 1) + b
End

Function QuinticTween:Float(b:Float, c:Float, t:Float, d:Float = 1)
	Local diff:Float = c - b
	t /= d / 2
	If (t < 1) Return diff / 2 * t * t * t * t * t + b
	t -= 2
	Return diff / 2 * (t * t * t * t * t + 2) + b
End

Function Tween:Float(p1:Float, p2:Float, t:Float)
	Return p1 + t * (p2 - p1)
End

Function TweenSmooth:Float(p1:Float, p2:Float, t:Float)
	Local v:Float = SmoothStep(t)
	Return p1 + v * (p2 - p1)
End

Function SmoothStep:Float(x:Float, interpSmooth:Int = 1)
	For Local i:Int = 0 Until interpSmooth
		x *= x * (3 - 2 * x)
	Next

	Return x' x*x * (3-2*x)
End

Function TweenUp:Float(p1:Float, p2:Float, t:Float)
	Local v:Float = SmoothStep(t)
	v = Pow(v, 2) 'power of 2.
	Return p1 + v * (p2 - p1)
End

Function TweenDown:Float(p1:Float, p2:Float, t:Float)
	Local v:Float = SmoothStep(t)
	v = 1 - Pow(1 - v, 2) 'InvSquared
	Return p1 + v * (p2 - p1)
End


Sprite animation are now Restart()-able

Sprite animation are now Restart()-able

Strict

Private

Import baseobject
Import director
Import mojo.graphics
Import vector2d

Public

Class Sprite Extends BaseObject
    Private

    Field image:Image
    Field frameTimer:Int
    Field currentFrame:Int
    Field frameCount:Int

    Public

    Field rotation:Float
    Field scale:Vector2D = New Vector2D(1, 1)
    Field loopAnimation:Bool
    Field frameSpeed:Int

    ' TODO: LoadImage() should be called in OnCreate()
    Method New(imageName:String, pos:Vector2D=Null)
        image = LoadImage(imageName)
        InitVectors(image.Width(), image.Height(), pos)
    End

    Method New(imageName:String, frameWidth:Int, frameHeight:Int, frameCount:Int, frameSpeed:Int, pos:Vector2D=Null)
        Self.frameCount = frameCount - 1
        Self.frameSpeed = frameSpeed

        image = LoadImage(imageName, frameWidth, frameHeight, frameCount)
        InitVectors(frameWidth, frameHeight, pos)
    End

    Method OnRender:Void()
        Super.OnRender()
        DrawImage(image, pos.x, pos.y, rotation, scale.x, scale.y, currentFrame)
    End

    Method OnUpdate:Void(delta:Float, frameTime:Float)
        Super.OnUpdate(delta, frameTime)

        If frameCount <= 0 Then Return
        If animationIsDone Then Return

        If frameTimer < frameSpeed
            frameTimer += frameTime
            Return
        End

        If currentFrame = frameCount
            If loopAnimation
                currentFrame = 1
            End
        Else
            currentFrame += 1
        End
        frameTimer = 0
    End

    Method Restart:Void()
        currentFrame = 0
    End

    Method Collide:Bool(checkPos:Vector2D)
        If checkPos.x < pos.x Or checkPos.x > pos.x + size.x Then Return False
        If checkPos.y < pos.y Or checkPos.y > pos.y + size.y Then Return False
        Return True
    End

    Method animationIsDone:Bool() Property
        If loopAnimation Then Return False
        Return (currentFrame = frameCount)
    End

    Private

    Method InitVectors:Void(width:Int, height:Int, pos:Vector2D=Null)
        If pos = Null
            Self.pos = New Vector2D(0, 0)
        Else
            Self.pos = pos
        End
        size = New Vector2D(width, height)
    End
End

Strict

Private

Import baseobject
Import director
Import mojo.graphics
Import vector2d

Public

Class Sprite Extends BaseObject
    Private

    Field image:Image
    Field frameTimer:Int
    Field currentFrame:Int
    Field frameCount:Int

    Public

    Field rotation:Float
    Field scale:Vector2D = New Vector2D(1, 1)
    Field loopAnimation:Bool
    Field frameSpeed:Int

    ' TODO: LoadImage() should be called in OnCreate()
    Method New(imageName:String, pos:Vector2D=Null)
        image = LoadImage(imageName)
        InitVectors(image.Width(), image.Height(), pos)
    End

    Method New(imageName:String, frameWidth:Int, frameHeight:Int, frameCount:Int, frameSpeed:Int, pos:Vector2D=Null)
        Self.frameCount = frameCount - 1
        Self.frameSpeed = frameSpeed

        image = LoadImage(imageName, frameWidth, frameHeight, frameCount)
        InitVectors(frameWidth, frameHeight, pos)
    End

    Method OnRender:Void()
        Super.OnRender()
        DrawImage(image, pos.x, pos.y, rotation, scale.x, scale.y, currentFrame)
    End

    Method OnUpdate:Void(delta:Float, frameTime:Float)
        Super.OnUpdate(delta, frameTime)

        If frameCount <= 0 Then Return
        If animationIsDone Then Return

        If frameTimer < frameSpeed
            frameTimer += frameTime
            Return
        End

        If currentFrame = frameCount
            If loopAnimation
                currentFrame = 1
            End
        Else
            currentFrame += 1
        End
        frameTimer = 0
    End

    Method Collide:Bool(checkPos:Vector2D)
        If checkPos.x < pos.x Or checkPos.x > pos.x + size.x Then Return False
        If checkPos.y < pos.y Or checkPos.y > pos.y + size.y Then Return False
        Return True
    End

    Method animationIsDone:Bool() Property
        If loopAnimation Then Return False
        Return (currentFrame = frameCount)
    End

    Private

    Method InitVectors:Void(width:Int, height:Int, pos:Vector2D=Null)
        If pos = Null
            Self.pos = New Vector2D(0, 0)
        Else
            Self.pos = pos
        End
        size = New Vector2D(width, height)
    End
End