Memory leak in FlxGroup.Destroy fixed

Memory leak in FlxGroup.Destroy fixed

#rem
	header:This module contains the FlxGroup class
#end
Strict

Import reflection

Import flxextern
Import flxbasic
Import flxobject
Import system.flxarray

Alias MonkeyGetClass = reflection.GetClass

#Rem
summary:This is an organizational class that can update and render a bunch of FlxBasics.
[i][b]NOTE:[/b] Although FlxGroup extends [a flxbasic.monkey.html]FlxBasic[/a], it will not automatically add itself to the global collisions quad tree, it will only add its members.[/i]
#End
Class FlxGroup Extends FlxBasic

	Global ClassObject:Object

	#Rem
	summary:See detail.
	Use with [a #Sort]Sort()[/a] to sort in ascending order.
	#End
	Const ASCENDING:Bool = False

	#Rem
	summary:See detail.
	Use with [a #Sort]Sort()[/a] to sort in descending order.
	#End
	Const DESCENDING:Bool = True

Private
	Field _maxSize:Int

	Field _marker:Int

	Field _length:Int

	Field _members:FlxBasic[]

	Field _sortIndex:FieldInfo

	Field _sortMethod:MethodInfo

	Field _sortDescending:Bool

Public
	#Rem
	summary:Constructor
	Params:
	[list]
	[*]maxSize:Int - the maximum capacity of this group.
	[/list]
	#End
	Method New(maxSize:Int = 0)
		Super.New()
		_maxSize = maxSize
		_marker = 0
		_length = 0
		_cameras = Null
	End Method

	#Rem
	summary:The number of entries in the members array.
	For performance and safety you should check this variable instead of members.length unless you really know what you're doing!
	#End
	Method Length:Int() Property
		Return _length
	End Method

	#Rem
	summary:See detail.
	Array of all the [a flxbasic.monkey.html]FlxBasic[/a]s that exist in this group.
	#End
	Method Members:FlxBasic[]() Property
		Return _members
	End Method

	#Rem
	summary:Override this function to handle any deleting or "shutdown" type operations you might need,such as removing traditional Monkey/Mojo children like Images objects.
	#End
	Method Destroy:Void()
		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) basic.Destroy()
			i+=1
		Wend

		_length = 0
		_members = _members.Resize(_length)
		_cameras = Null

		_sortIndex = Null
		_sortMethod = Null

		Super.Destroy()
	End Method

	#Rem
	summary:Just making sure we don't increment the active objects count.
	#End
	Method PreUpdate:Void()

	End Method

	#Rem
	summary:Automatically goes through and calls update on everything you added.
	#End
	Method Update:Void()
		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists And basic.active) Then
				basic.PreUpdate()
				basic.Update()
				If (basic.HasTween) basic.UpdateTweens()
				basic.PostUpdate()
			End If
			i+=1
		Wend

		If (HasTween) UpdateTweens()
	End Method

	#Rem
	summary:Automatically goes through and calls render on everything you added.
	#End
	Method Draw:Void()
		If (_cameras <> Null And Not _cameras.Contains(FlxG._CurrentCamera.ID)) Return

		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists And basic.visible) basic.Draw()
			i+=1
		Wend
	End Method

	#Rem
	summary:The maximum capacity of this group.  Default is 0, meaning no max capacity, and the group can just grow.
	#End
	Method MaxSize:Int() Property
		Return _maxSize
	End Method

	Method MaxSize:Void(size:Int) Property
		_maxSize = size

		If (_marker >= _maxSize) _marker = 0
		If (_maxSize = 0 Or _maxSize >= _length) Return

		Local basic:FlxBasic
		Local i:Int = _maxSize
		Local l:Int = _length

		While(i < l)
			basic = _members[i]
			If (basic <> Null) basic.Destroy()
			i+=1
		Wend

		_members = _members.Resize(_maxSize)
		_length = _maxSize
	End Method

	#Rem
	summary:See details.
	Adds a new [a flxbasic.monkey.html]FlxBasic[/a] subclass ([a flxbasic.monkey.html]FlxBasic[/a], [a flxsprite.monkey.html]FlxSprite[/a], Enemy, etc) to the group. FlxGroup will try to replace a null member of the array first. Failing that, FlxGroup will add it to the end of the member array, assuming there is room for it, and doubling the size of the array if necessary.

	[b]WARNING[/b]: If the group has a maxSize that has already been met, the object will NOT be added to the group!

	Params:
	[list]
	[*][a flxbasic.monkey.html]object:FlxBasic[/a] - the object you want to add to the group.
	[/list]
	Return the same [a flxbasic.monkey.html]FlxBasic[/a] object that was passed in.
	#End
	Method Add:FlxBasic(object:FlxBasic)
		If (_IndexOf(object) >= 0) Return object

		Local i:Int = 0

		While (i < _length)
			If (_members[i] = Null) Then
				_members[i] = object
				If (i >= _length) _length = i+1
				Return object
			End If
			i+=1
		Wend

		If (_maxSize > 0) Then
			If (_length = _members.Length()) Then
				If (_length >= _maxSize) Then
					Return object
				ElseIf (_length*2 + 10 <= _maxSize)
					_members = _members.Resize(_length*2 + 10)
				Else
					_members = _members.Resize(_maxSize)
				End If
			End if
		Else
			If (_length = _members.Length()) Then
				_members = _members.Resize(_length*2 + 10)
			End If
		End If

		_members[i] = object
		_length+=1

		Return object
	End Method

	#Rem
	summary:Recycling is designed to help you reuse game objects without always re-allocating or "newing" them.
	If you specified a maximum size for this group (like in [a flxemitter.monkey.html]FlxEmitter[/a],then recycle will employ what we're calling "rotating" recycling. Recycle() will first check to see if the group is at capacity yet. If group is not yet at capacity, Recycle() returns a new object. If the group IS at capacity, then Recycle() just returns the next object in line

	If you did NOT specify a maximum size for this group, then Recycle() will employ what we're calling "grow-style" recycling. Recycle() will return either the first object with exists = false, or, finding none, add a new object to the array, doubling the size of the array if necessary.

	[b]WARNING:[/b] If this function needs to create a new object, and no object class was provided, it will return null instead of a valid object!

	Params:
	[list]
	[*]objectClass:FlxClass - the type objectClass implements FlxClass you want to recycle (e.g. FlxSpriteClass, EvilRobotClass, etc). Do NOT "new" the class in the parameter!
	[/list]
	Return a reference to the object that was created. Don't forget to cast it back to the objectClass you want (e.g. myObject = myObjectClass(myGroup.recycle(myObjectClassClass))).
	#End
	Method Recycle:FlxBasic(objectClass:ClassInfo = null)
		If (_maxSize > 0) Then
			If (_length < _maxSize) Then
				If (objectClass = Null) Return Null
				Return Add(FlxBasic(objectClass.NewInstance()))
			Else
				Local basic:FlxBasic = _members[_marker]
				_marker+=1
				If (_marker >= _maxSize) _marker = 0
				Return basic
			End If
		Else
			Local basic:FlxBasic = GetFirstAvailable(objectClass)
			If (basic <> Null) Return basic
			If (objectClass = Null) Return Null
			Return Add(FlxBasic(objectClass.NewInstance()))
		End If
	End Method

	#Rem
	summary:Removes an object from the group.
	Params:
	[list]
	[*][a flxbasic.monkey.html]object:FlxBasic[/a] - The [a flxbasic.monkey.html]FlxBasic[/a] you want to remove.
	[*]splice:Bool - whether the object should be cut from the array entirely or not.
	[/list]
	Return the removed object.
	#End
	Method Remove:FlxBasic(object:FlxBasic, splice:Bool = False)
		Local index:Int = _IndexOf(object)

		If (index < 0) Return Null

		If (splice) Then
			Local i:Int = index
			Local l:Int = _length

			While(i < _length - 1)
				_members[i] = _members[i+1]
				i+=1
			Wend

			_length-=1
		Else
			_members[index] = Null
		End If

		Return object
	End Method

	#Rem
	summary:Replaces an existing [a flxbasic.monkey.html]FlxBasic[/a] with a new one.
	Params:
	[list]
	[*][a flxbasic.monkey.html]oldObject:FlxBasic[/a] - the object you want to replace.
	[*][a flxbasic.monkey.html]newObject:FlxBasic[/a] - the new object you want to use instead.
	[/list]
	Return the new object.
	#End
	Method Replace:FlxBasic(oldObject:FlxBasic, newObject:FlxBasic)
		Local index:Int  = _IndexOf(oldObject)
		If (index < 0) Return Null

		_members[index] = newObject
		Return newObject
	End Method

	#Rem
	summary:Call this function to sort the group according to a particular value and order.
	For example, to sort game objects for Zelda-style overlaps you might call myGroup.Sort(FlxObject.YComparator, FlxGroup.ASCENDING) at the bottom of your [a flxstate.monkey.html]FlxState.Update()[/a] override. To sort all existing objects after a big explosion or bomb attack, you might call myGroup.Sort(FlxBasic.ExistsComparator, FlxGroup.DESCENDING)
	Params:
	[list]
	[*]comparator:FlxBasicComparator - The FlxBasicComparator you want to sort on. Default value is FlxObject.YComparator.
	[*]order:Bool - A FlxGroup constant that defines the sort order. Possible values are ASCENDING and DESCENDING. Default value is ASCENDING.
	[/list]
	Return the new object.
	#End
	Method Sort:Void(index:String = "y", order:Bool = ASCENDING)
		_sortIndex = GetFirstNotNull().GetClassInfo().GetField(index)
		_sortDescending = order

		Select _sortIndex.Type.Name

			Case FloatClass().Name
				_QSortFloat(0, _length - 1)

			Case IntClass().Name
				_QSortInt(0, _length - 1)

			Case BoolClass().Name
				_QSortBool(0, _length - 1)

			Case StringClass().Name
				_QSortString(0, _length - 1)

			Default
				_sortMethod = _sortIndex.Type.GetMethod("Compare",[_sortIndex.Type])
				If (_sortMethod = Null) _sortMethod = _sortIndex.Type.GetMethod("Compare",[GetClass("Object")])

				If (_sortMethod <> Null) Then
					_QSortObject(0, _length - 1)
				End If
		End Select
	End Method

	Method SetAll:Void(variableName:String, value:Object, recurse:Bool = True)
		Local basic:FlxBasic = GetFirstNotNull()
		If (basic = Null) Return

		Local f:FieldInfo = basic.GetClassInfo().GetField(variableName)

		If (f = Null) Then
			_SetAllProperties(variableName, value, recurse)
			Return
		End If

		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (recurse And FlxGroup(basic) <> Null) Then
					FlxGroup(basic).SetAll(variableName, value, recurse)
				Else
					f.SetValue(basic, value)
				End If
			End If
			i+=1
		Wend
	End Method

	Method CallAll:Void(functionName:String, recurse:Bool = True)
		Local basic:FlxBasic = GetFirstNotNull()
		If (basic = Null) Return

		Local m:MethodInfo = basic.GetClassInfo().GetMethod(functionName,[])
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (recurse And FlxGroup(basic) <> Null) Then
					FlxGroup(basic).CallAll(functionName, recurse)
				Else
					m.Invoke(basic,[])
				End If
			End If
			i+=1
		Wend
	End Method

	Method GetFirstAvailable:FlxBasic(objectClass:ClassInfo = null)
		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And Not basic.exists And
					(objectClass = Null Or basic.GetClassInfo().ExtendsClass(objectClass))) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstNull:Int()
		Local i:Int = 0

		While(i < _length)
			If (_members[i] = Null) Return i
			i+=1
		Wend

		Return -1
	End Method

	Method GetFirstNotNull:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstExtant:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstAlive:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists And basic.alive) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstDead:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And Not basic.alive) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method CountLiving:Int()
		Local count:Int = -1
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (count < 0) count = 0
				If (basic.exists And basic.alive) count+=1
			End If
			i+=1
		Wend

		Return count
	End Method

	Method CountDead:Int()
		Local count:Int = -1
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (count < 0) count = 0
				If (Not basic.alive) count+=1
			End If
			i+=1
		Wend

		Return count
	End Method

	Method GetRandom:FlxBasic(startIndex:Int = 0, length:Int = 0)
		If (length = 0) length = _length
		Return FlxArray<FlxBasic>.GetSafeRandom(_members, startIndex, length)
	End Method

	Method Clear:Void()
		_length = 0
	End Method

	Method ReviveAll:Void()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And Not basic.exists) basic.Revive()
			i+=1
		Wend

		Super.Revive()
	End Method

	Method Kill:Void()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists) basic.Kill()
			i+=1
		Wend

		Super.Kill()
	End Method

	Method ObjectEnumerator:Enumerator()
		Return New Enumerator(Self)
	End

Private
	Method _IndexOf:Int(object:FlxBasic)
		Local i:Int = 0

		While(i < _length)
			If (_members[i] = object) Return i
			i+=1
		Wend

		Return -1
	End Method

	Method _QSortBool:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortBool(left, right, pivot)

			_QSortBool(left, newPivot - 1)
			_QSortBool(newPivot + 1, right)
		End If
	End Method

	Method _DoSortBool:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (Int(UnboxBool(_sortIndex.GetValue(basicToCompare))) - Int(UnboxBool(_sortIndex.GetValue(basic))) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (Int(UnboxBool(_sortIndex.GetValue(basicToCompare))) - Int(UnboxBool(_sortIndex.GetValue(basic))) <= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortInt:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortInt(left, right, pivot)

			_QSortInt(left, newPivot - 1)
			_QSortInt(newPivot + 1, right)
		End If
	End Method

	Method _DoSortInt:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxInt(_sortIndex.GetValue(basicToCompare)) >= UnboxInt(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxInt(_sortIndex.GetValue(basicToCompare)) <= UnboxInt(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortFloat:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortFloat(left, right, pivot)

			_QSortFloat(left, newPivot - 1)
			_QSortFloat(newPivot + 1, right)
		End If
	End Method

	Method _DoSortFloat:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxFloat(_sortIndex.GetValue(basicToCompare)) >= UnboxFloat(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxFloat(_sortIndex.GetValue(basicToCompare)) <= UnboxFloat(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortString:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortString(left, right, pivot)

			_QSortString(left, newPivot - 1)
			_QSortString(newPivot + 1, right)
		End If
	End Method

	Method _DoSortString:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxString(_sortIndex.GetValue(basicToCompare)).Compare(UnboxString(_sortIndex.GetValue(basic))) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxString(_sortIndex.GetValue(basicToCompare)).Compare(UnboxString(_sortIndex.GetValue(basic))) <= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortObject:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortObject(left, right, pivot)

			_QSortObject(left, newPivot - 1)
			_QSortObject(newPivot + 1, right)
		End If
	End Method

	Method _DoSortObject:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxInt(_sortMethod.Invoke(_sortIndex.GetValue(basicToCompare),[_sortIndex.GetValue(basic)])) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxInt(_sortMethod.Invoke(_sortIndex.GetValue(basicToCompare),[_sortIndex.GetValue(basic)])) <= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _SetAllProperties:Void(variableName:String, value:Object, recurse:Bool = True)
		Local basic:FlxBasic = GetFirstNotNull()
		If (basic = Null) Return

		Local prop:MethodInfo

		prop = basic.GetClassInfo().GetMethod(variableName,[MonkeyGetClass(value)])
		If (prop = Null) Return

		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (recurse And FlxGroup(basic) <> Null) Then
					FlxGroup(basic).SetAll(variableName, value, recurse)
				Else
					prop.Invoke(basic,[value])
				End If
			End If
			i+=1
		Wend
	End Method

End Class

Class Enumerator

	Method New(group:FlxGroup)
		_group = group
	End

	Method HasNext:Bool()
		Return _index < _group.Length
	End

	Method NextObject:FlxBasic()
		_index+=1
		Return _group.Members[_index-1]
	End

Private

	Field _group:FlxGroup
	Field _index:Int

End

#Rem
footer:Flixel is an open source game-making library that is completely free for personal or commercial use.
[quote]Copyright: Flixel - 2009-2011 Adam 'Atomic' Saltsman
Copyright: Monkey port - 2011 Arthur 'devolonter' Bikmullin

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.

NOTE FROM THE AUTHOR: As far as I know, you only have to include
this license if you are redistributing source code that includes
the Flixel library.  There is no need (or way, afaik) to include
it in your compiled flash games and apps![/quote]
#End

#rem
	header:This module contains the FlxGroup class
#end
Strict

Import reflection

Import flxextern
Import flxbasic
Import flxobject
Import system.flxarray

Alias MonkeyGetClass = reflection.GetClass

#Rem
summary:This is an organizational class that can update and render a bunch of FlxBasics.
[i][b]NOTE:[/b] Although FlxGroup extends [a flxbasic.monkey.html]FlxBasic[/a], it will not automatically add itself to the global collisions quad tree, it will only add its members.[/i]
#End
Class FlxGroup Extends FlxBasic

	Global ClassObject:Object

	#Rem
	summary:See detail.
	Use with [a #Sort]Sort()[/a] to sort in ascending order.
	#End
	Const ASCENDING:Bool = False

	#Rem
	summary:See detail.
	Use with [a #Sort]Sort()[/a] to sort in descending order.
	#End
	Const DESCENDING:Bool = True

Private
	Field _maxSize:Int

	Field _marker:Int

	Field _length:Int

	Field _members:FlxBasic[]

	Field _sortIndex:FieldInfo

	Field _sortMethod:MethodInfo

	Field _sortDescending:Bool

Public
	#Rem
	summary:Constructor
	Params:
	[list]
	[*]maxSize:Int - the maximum capacity of this group.
	[/list]
	#End
	Method New(maxSize:Int = 0)
		Super.New()
		_maxSize = maxSize
		_marker = 0
		_length = 0
		_cameras = Null
	End Method

	#Rem
	summary:The number of entries in the members array.
	For performance and safety you should check this variable instead of members.length unless you really know what you're doing!
	#End
	Method Length:Int() Property
		Return _length
	End Method

	#Rem
	summary:See detail.
	Array of all the [a flxbasic.monkey.html]FlxBasic[/a]s that exist in this group.
	#End
	Method Members:FlxBasic[]() Property
		Return _members
	End Method

	#Rem
	summary:Override this function to handle any deleting or "shutdown" type operations you might need,such as removing traditional Monkey/Mojo children like Images objects.
	#End
	Method Destroy:Void()
		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) basic.Destroy()
			i+=1
		Wend

		_length = 0
		_members = _members.Resize(_length)
		_cameras = Null

		Super.Destroy()
	End Method

	#Rem
	summary:Just making sure we don't increment the active objects count.
	#End
	Method PreUpdate:Void()

	End Method

	#Rem
	summary:Automatically goes through and calls update on everything you added.
	#End
	Method Update:Void()
		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists And basic.active) Then
				basic.PreUpdate()
				basic.Update()
				If (basic.HasTween) basic.UpdateTweens()
				basic.PostUpdate()
			End If
			i+=1
		Wend

		If (HasTween) UpdateTweens()
	End Method

	#Rem
	summary:Automatically goes through and calls render on everything you added.
	#End
	Method Draw:Void()
		If (_cameras <> Null And Not _cameras.Contains(FlxG._CurrentCamera.ID)) Return

		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists And basic.visible) basic.Draw()
			i+=1
		Wend
	End Method

	#Rem
	summary:The maximum capacity of this group.  Default is 0, meaning no max capacity, and the group can just grow.
	#End
	Method MaxSize:Int() Property
		Return _maxSize
	End Method

	Method MaxSize:Void(size:Int) Property
		_maxSize = size

		If (_marker >= _maxSize) _marker = 0
		If (_maxSize = 0 Or _maxSize >= _length) Return

		Local basic:FlxBasic
		Local i:Int = _maxSize
		Local l:Int = _length

		While(i < l)
			basic = _members[i]
			If (basic <> Null) basic.Destroy()
			i+=1
		Wend

		_members = _members.Resize(_maxSize)
		_length = _maxSize
	End Method

	#Rem
	summary:See details.
	Adds a new [a flxbasic.monkey.html]FlxBasic[/a] subclass ([a flxbasic.monkey.html]FlxBasic[/a], [a flxsprite.monkey.html]FlxSprite[/a], Enemy, etc) to the group. FlxGroup will try to replace a null member of the array first. Failing that, FlxGroup will add it to the end of the member array, assuming there is room for it, and doubling the size of the array if necessary.

	[b]WARNING[/b]: If the group has a maxSize that has already been met, the object will NOT be added to the group!

	Params:
	[list]
	[*][a flxbasic.monkey.html]object:FlxBasic[/a] - the object you want to add to the group.
	[/list]
	Return the same [a flxbasic.monkey.html]FlxBasic[/a] object that was passed in.
	#End
	Method Add:FlxBasic(object:FlxBasic)
		If (_IndexOf(object) >= 0) Return object

		Local i:Int = 0

		While (i < _length)
			If (_members[i] = Null) Then
				_members[i] = object
				If (i >= _length) _length = i+1
				Return object
			End If
			i+=1
		Wend

		If (_maxSize > 0) Then
			If (_length = _members.Length()) Then
				If (_length >= _maxSize) Then
					Return object
				ElseIf (_length*2 + 10 <= _maxSize)
					_members = _members.Resize(_length*2 + 10)
				Else
					_members = _members.Resize(_maxSize)
				End If
			End if
		Else
			If (_length = _members.Length()) Then
				_members = _members.Resize(_length*2 + 10)
			End If
		End If

		_members[i] = object
		_length+=1

		Return object
	End Method

	#Rem
	summary:Recycling is designed to help you reuse game objects without always re-allocating or "newing" them.
	If you specified a maximum size for this group (like in [a flxemitter.monkey.html]FlxEmitter[/a],then recycle will employ what we're calling "rotating" recycling. Recycle() will first check to see if the group is at capacity yet. If group is not yet at capacity, Recycle() returns a new object. If the group IS at capacity, then Recycle() just returns the next object in line

	If you did NOT specify a maximum size for this group, then Recycle() will employ what we're calling "grow-style" recycling. Recycle() will return either the first object with exists = false, or, finding none, add a new object to the array, doubling the size of the array if necessary.

	[b]WARNING:[/b] If this function needs to create a new object, and no object class was provided, it will return null instead of a valid object!

	Params:
	[list]
	[*]objectClass:FlxClass - the type objectClass implements FlxClass you want to recycle (e.g. FlxSpriteClass, EvilRobotClass, etc). Do NOT "new" the class in the parameter!
	[/list]
	Return a reference to the object that was created. Don't forget to cast it back to the objectClass you want (e.g. myObject = myObjectClass(myGroup.recycle(myObjectClassClass))).
	#End
	Method Recycle:FlxBasic(objectClass:ClassInfo = null)
		If (_maxSize > 0) Then
			If (_length < _maxSize) Then
				If (objectClass = Null) Return Null
				Return Add(FlxBasic(objectClass.NewInstance()))
			Else
				Local basic:FlxBasic = _members[_marker]
				_marker+=1
				If (_marker >= _maxSize) _marker = 0
				Return basic
			End If
		Else
			Local basic:FlxBasic = GetFirstAvailable(objectClass)
			If (basic <> Null) Return basic
			If (objectClass = Null) Return Null
			Return Add(FlxBasic(objectClass.NewInstance()))
		End If
	End Method

	#Rem
	summary:Removes an object from the group.
	Params:
	[list]
	[*][a flxbasic.monkey.html]object:FlxBasic[/a] - The [a flxbasic.monkey.html]FlxBasic[/a] you want to remove.
	[*]splice:Bool - whether the object should be cut from the array entirely or not.
	[/list]
	Return the removed object.
	#End
	Method Remove:FlxBasic(object:FlxBasic, splice:Bool = False)
		Local index:Int = _IndexOf(object)

		If (index < 0) Return Null

		If (splice) Then
			Local i:Int = index
			Local l:Int = _length

			While(i < _length - 1)
				_members[i] = _members[i+1]
				i+=1
			Wend

			_length-=1
		Else
			_members[index] = Null
		End If

		Return object
	End Method

	#Rem
	summary:Replaces an existing [a flxbasic.monkey.html]FlxBasic[/a] with a new one.
	Params:
	[list]
	[*][a flxbasic.monkey.html]oldObject:FlxBasic[/a] - the object you want to replace.
	[*][a flxbasic.monkey.html]newObject:FlxBasic[/a] - the new object you want to use instead.
	[/list]
	Return the new object.
	#End
	Method Replace:FlxBasic(oldObject:FlxBasic, newObject:FlxBasic)
		Local index:Int  = _IndexOf(oldObject)
		If (index < 0) Return Null

		_members[index] = newObject
		Return newObject
	End Method

	#Rem
	summary:Call this function to sort the group according to a particular value and order.
	For example, to sort game objects for Zelda-style overlaps you might call myGroup.Sort(FlxObject.YComparator, FlxGroup.ASCENDING) at the bottom of your [a flxstate.monkey.html]FlxState.Update()[/a] override. To sort all existing objects after a big explosion or bomb attack, you might call myGroup.Sort(FlxBasic.ExistsComparator, FlxGroup.DESCENDING)
	Params:
	[list]
	[*]comparator:FlxBasicComparator - The FlxBasicComparator you want to sort on. Default value is FlxObject.YComparator.
	[*]order:Bool - A FlxGroup constant that defines the sort order. Possible values are ASCENDING and DESCENDING. Default value is ASCENDING.
	[/list]
	Return the new object.
	#End
	Method Sort:Void(index:String = "y", order:Bool = ASCENDING)
		_sortIndex = GetFirstNotNull().GetClassInfo().GetField(index)
		_sortDescending = order

		Select _sortIndex.Type.Name

			Case FloatClass().Name
				_QSortFloat(0, _length - 1)

			Case IntClass().Name
				_QSortInt(0, _length - 1)

			Case BoolClass().Name
				_QSortBool(0, _length - 1)

			Case StringClass().Name
				_QSortString(0, _length - 1)

			Default
				_sortMethod = _sortIndex.Type.GetMethod("Compare",[_sortIndex.Type])
				If (_sortMethod = Null) _sortMethod = _sortIndex.Type.GetMethod("Compare",[GetClass("Object")])

				If (_sortMethod <> Null) Then
					_QSortObject(0, _length - 1)
				End If
		End Select
	End Method

	Method SetAll:Void(variableName:String, value:Object, recurse:Bool = True)
		Local basic:FlxBasic = GetFirstNotNull()
		If (basic = Null) Return

		Local f:FieldInfo = basic.GetClassInfo().GetField(variableName)

		If (f = Null) Then
			_SetAllProperties(variableName, value, recurse)
			Return
		End If

		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (recurse And FlxGroup(basic) <> Null) Then
					FlxGroup(basic).SetAll(variableName, value, recurse)
				Else
					f.SetValue(basic, value)
				End If
			End If
			i+=1
		Wend
	End Method

	Method CallAll:Void(functionName:String, recurse:Bool = True)
		Local basic:FlxBasic = GetFirstNotNull()
		If (basic = Null) Return

		Local m:MethodInfo = basic.GetClassInfo().GetMethod(functionName,[])
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (recurse And FlxGroup(basic) <> Null) Then
					FlxGroup(basic).CallAll(functionName, recurse)
				Else
					m.Invoke(basic,[])
				End If
			End If
			i+=1
		Wend
	End Method

	Method GetFirstAvailable:FlxBasic(objectClass:ClassInfo = null)
		Local basic:FlxBasic
		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And Not basic.exists And
					(objectClass = Null Or basic.GetClassInfo().ExtendsClass(objectClass))) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstNull:Int()
		Local i:Int = 0

		While(i < _length)
			If (_members[i] = Null) Return i
			i+=1
		Wend

		Return -1
	End Method

	Method GetFirstNotNull:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstExtant:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstAlive:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists And basic.alive) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method GetFirstDead:FlxBasic()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And Not basic.alive) Return basic
			i+=1
		Wend

		Return Null
	End Method

	Method CountLiving:Int()
		Local count:Int = -1
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (count < 0) count = 0
				If (basic.exists And basic.alive) count+=1
			End If
			i+=1
		Wend

		Return count
	End Method

	Method CountDead:Int()
		Local count:Int = -1
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (count < 0) count = 0
				If (Not basic.alive) count+=1
			End If
			i+=1
		Wend

		Return count
	End Method

	Method GetRandom:FlxBasic(startIndex:Int = 0, length:Int = 0)
		If (length = 0) length = _length
		Return FlxArray<FlxBasic>.GetSafeRandom(_members, startIndex, length)
	End Method

	Method Clear:Void()
		_length = 0
	End Method

	Method ReviveAll:Void()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And Not basic.exists) basic.Revive()
			i+=1
		Wend

		Super.Revive()
	End Method

	Method Kill:Void()
		Local i:Int = 0
		Local basic:FlxBasic

		While(i < _length)
			basic = _members[i]
			If (basic <> Null And basic.exists) basic.Kill()
			i+=1
		Wend

		Super.Kill()
	End Method

	Method ObjectEnumerator:Enumerator()
		Return New Enumerator(Self)
	End

Private
	Method _IndexOf:Int(object:FlxBasic)
		Local i:Int = 0

		While(i < _length)
			If (_members[i] = object) Return i
			i+=1
		Wend

		Return -1
	End Method

	Method _QSortBool:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortBool(left, right, pivot)

			_QSortBool(left, newPivot - 1)
			_QSortBool(newPivot + 1, right)
		End If
	End Method

	Method _DoSortBool:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (Int(UnboxBool(_sortIndex.GetValue(basicToCompare))) - Int(UnboxBool(_sortIndex.GetValue(basic))) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (Int(UnboxBool(_sortIndex.GetValue(basicToCompare))) - Int(UnboxBool(_sortIndex.GetValue(basic))) <= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortInt:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortInt(left, right, pivot)

			_QSortInt(left, newPivot - 1)
			_QSortInt(newPivot + 1, right)
		End If
	End Method

	Method _DoSortInt:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxInt(_sortIndex.GetValue(basicToCompare)) >= UnboxInt(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxInt(_sortIndex.GetValue(basicToCompare)) <= UnboxInt(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortFloat:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortFloat(left, right, pivot)

			_QSortFloat(left, newPivot - 1)
			_QSortFloat(newPivot + 1, right)
		End If
	End Method

	Method _DoSortFloat:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxFloat(_sortIndex.GetValue(basicToCompare)) >= UnboxFloat(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxFloat(_sortIndex.GetValue(basicToCompare)) <= UnboxFloat(_sortIndex.GetValue(basic))) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortString:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortString(left, right, pivot)

			_QSortString(left, newPivot - 1)
			_QSortString(newPivot + 1, right)
		End If
	End Method

	Method _DoSortString:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxString(_sortIndex.GetValue(basicToCompare)).Compare(UnboxString(_sortIndex.GetValue(basic))) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxString(_sortIndex.GetValue(basicToCompare)).Compare(UnboxString(_sortIndex.GetValue(basic))) <= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _QSortObject:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSortObject(left, right, pivot)

			_QSortObject(left, newPivot - 1)
			_QSortObject(newPivot + 1, right)
		End If
	End Method

	Method _DoSortObject:Int(left:Int, right:Int, pivot:Int)
		Local basic:FlxBasic = _members[pivot]

		_members[pivot] = _members[right]
		_members[right] = basic

		Local store:Int = left
		Local basicToCompare:FlxBasic
		Local i:Int = left

		While (i < right)
			basicToCompare = _members[i]

			If (_sortDescending) Then
				If (UnboxInt(_sortMethod.Invoke(_sortIndex.GetValue(basicToCompare),[_sortIndex.GetValue(basic)])) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (UnboxInt(_sortMethod.Invoke(_sortIndex.GetValue(basicToCompare),[_sortIndex.GetValue(basic)])) <= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			End If

			i+=1
		Wend

		basic = _members[store]
		_members[store] = _members[right]
		_members[right] = basic
		Return store
	End Method

	Method _SetAllProperties:Void(variableName:String, value:Object, recurse:Bool = True)
		Local basic:FlxBasic = GetFirstNotNull()
		If (basic = Null) Return

		Local prop:MethodInfo

		prop = basic.GetClassInfo().GetMethod(variableName,[MonkeyGetClass(value)])
		If (prop = Null) Return

		Local i:Int = 0

		While(i < _length)
			basic = _members[i]
			If (basic <> Null) Then
				If (recurse And FlxGroup(basic) <> Null) Then
					FlxGroup(basic).SetAll(variableName, value, recurse)
				Else
					prop.Invoke(basic,[value])
				End If
			End If
			i+=1
		Wend
	End Method

End Class

Class Enumerator

	Method New(group:FlxGroup)
		_group = group
	End

	Method HasNext:Bool()
		Return _index < _group.Length
	End

	Method NextObject:FlxBasic()
		_index+=1
		Return _group.Members[_index-1]
	End

Private

	Field _group:FlxGroup
	Field _index:Int

End

#Rem
footer:Flixel is an open source game-making library that is completely free for personal or commercial use.
[quote]Copyright: Flixel - 2009-2011 Adam 'Atomic' Saltsman
Copyright: Monkey port - 2011 Arthur 'devolonter' Bikmullin

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.

NOTE FROM THE AUTHOR: As far as I know, you only have to include
this license if you are redistributing source code that includes
the Flixel library.  There is no need (or way, afaik) to include
it in your compiled flash games and apps![/quote]
#End


Revert "Real elpsed time returned"

Revert "Real elpsed time returned"

This reverts commit 9e49721dc433b473dad34bf5f37b6efcc7e5d514.

Strict

Import mojo

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _fps:Int

	Field _fpsCounter:Int

	Field _lastFpsCounter:Int

	Field _fpsTime:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:FlxClass, zoom:Float = 1, framerate:Int = 60, useSystemCursor:Bool = False)
		_lostFocus = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor
		useVirtualResolution = True
		If (Not useSystemCursor) HideMouse()
		_debuggerUp = False

		_state = Null

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False

		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 60
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False
	End Method

	Method OnCreate:Int()
		_Reset()
		_InitData()
		_Step()

		_soundTrayX	= (FlxG.Width / 2) * FlxCamera.DefaultZoom - (_soundTrayWidth / 2)
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		Return 0
	End Method

	Method OnUpdate:Int()
		#If TARGET <> "ios" Or TARGET <> "android"
			If (useSoundHotKeys) Then
				If (KeyHit(KEY_0)) Then
					FlxG.Mute = Not FlxG.Mute

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

					_ShowSoundTray()
				End If

				If (KeyHit(KEY_MINUS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() - .1)
					_ShowSoundTray()
				End If

				If (KeyHit(KEY_EQUALS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() + .1)
					_ShowSoundTray()
				End If
			End If
		#End

		Return 0
	End Method

	Method OnRender:Int()
		If (_fps < 0) Then
			_fpsTime = Millisecs()
			_fps = FlxG.Framerate
			_lastFpsCounter = _fps
		Else
			If (Millisecs() - _fpsTime > 1000) Then
				If (Abs(_lastFpsCounter - _fpsCounter) < 10) Then
					_fps = _fpsCounter
				End If

				_lastFpsCounter = _fpsCounter
				_fpsCounter = 0
				_fpsTime = Millisecs()
			Else
				_fpsCounter += 1
			End If
		End If

		'Real elapsed very unstable in Monkey. TODO!
		FlxG.Elapsed = FlxG.TimeScale * (1.0 / _fps)

		_UpdateSoundTray()
		_Step()

		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

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

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

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			FlxG._CurrentCameraID = i

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

		#If TARGET <> "ios" Or TARGET <> "android"
			If (_soundTrayVisible) Then
				Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
				If (FlxG.Mute) globalVolume = 0

				Local bx:Int = 20
				Local by:Int = 28

				If (FlxG._LastDrawingColor <> FlxG.WHITE) Then
					SetColor(255, 255, 255)
					FlxG._LastDrawingColor = FlxG.WHITE
				End If

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

				Local i:Int = 0
				While (i < 10)
					If (i < globalVolume) Then
						If (FlxG._LastDrawingAlpha <> 1) Then
							SetAlpha(1)
							FlxG._LastDrawingAlpha = 1
						End If

					Else
						If (FlxG._LastDrawingAlpha <> .5) Then
							SetAlpha(.5)
							FlxG._LastDrawingAlpha = .5
						End If
					End If

					DrawRect(bx, by, 8, i * 2)

					bx += 12
					by -= 2
					i += 1
				Wend

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.Mouse.Draw()

		#If CONFIG = "debug"
			DrawText("FPS: " + _fps, 10, 10)
		#End

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And useSystemCursor) Then
			HideMouse()
		End If
		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play(FlxG.DATA_PREFIX + "beep")
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.CreateInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

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

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

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

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _UpdateSoundTray:Void()
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= FlxG.Elapsed

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (FlxG.Elapsed * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Update:Void()
		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()
		FlxG.UpdateCameras()
	End Method

	Method _Reset:Void()
		SetUpdateRate(FlxG.Framerate)
		Seed = SystemMillisecs()

		FlxG.DeviceWidth = DeviceWidth()
		FlxG.DeviceHeight = DeviceHeight()

		If (useVirtualResolution) Then
			FlxG._DeviceScaleFactorX = FlxG.DeviceWidth / Float(FlxG.Width)
			FlxG._DeviceScaleFactorY = FlxG.DeviceHeight / Float(FlxG.Height)
		Else
			FlxG._DeviceScaleFactorX = 1
			FlxG._DeviceScaleFactorY = 1
		End If

		_step = 1000.0 / FlxG.Framerate
		_fps = -1
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxG.DATA_PREFIX + FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) + ".png")
		Next

		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "default", FlxG.DATA_PREFIX + "default.png")
		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "button", FlxG.DATA_PREFIX + "button.png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor(FlxG.DATA_PREFIX + "cursor", FlxG.DATA_PREFIX + "cursor.png")
		FlxAssetsManager.AddSound(FlxG.DATA_PREFIX + "beep", FlxG.DATA_PREFIX + "beep.mp3")

		Self.OnContentInit()
	End Method
End Class

Strict

Import mojo

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _lastUpdateTime:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:FlxClass, zoom:Float = 1, framerate:Int = 60, useSystemCursor:Bool = False)
		_lostFocus = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor
		useVirtualResolution = True
		If (Not useSystemCursor) HideMouse()
		_debuggerUp = False

		_state = Null

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False

		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 60
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False
	End Method

	Method OnCreate:Int()
		_Reset()
		_InitData()
		_Step()

		_soundTrayX	= (FlxG.Width / 2) * FlxCamera.DefaultZoom - (_soundTrayWidth / 2)
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		Return 0
	End Method

	Method OnUpdate:Int()
		If (_lastUpdateTime <> 0) Then
			FlxG.Elapsed = FlxG.TimeScale * ((Millisecs() - _lastUpdateTime) / 1000.0)
		End If

		_lastUpdateTime = Millisecs()

		#If TARGET <> "ios" Or TARGET <> "android"
			If (useSoundHotKeys) Then
				If (KeyHit(KEY_0)) Then
					FlxG.Mute = Not FlxG.Mute

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

					_ShowSoundTray()
				End If

				If (KeyHit(KEY_MINUS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() - .1)
					_ShowSoundTray()
				End If

				If (KeyHit(KEY_EQUALS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() + .1)
					_ShowSoundTray()
				End If
			End If
		#End

		_UpdateSoundTray()
		_Step()

		Return 0
	End Method

	Method OnRender:Int()
		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

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

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

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			FlxG._CurrentCameraID = i

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

		#If TARGET <> "ios" Or TARGET <> "android"
			If (_soundTrayVisible) Then
				Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
				If (FlxG.Mute) globalVolume = 0

				Local bx:Int = 20
				Local by:Int = 28

				If (FlxG._LastDrawingColor <> FlxG.WHITE) Then
					SetColor(255, 255, 255)
					FlxG._LastDrawingColor = FlxG.WHITE
				End If

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

				Local i:Int = 0
				While (i < 10)
					If (i < globalVolume) Then
						If (FlxG._LastDrawingAlpha <> 1) Then
							SetAlpha(1)
							FlxG._LastDrawingAlpha = 1
						End If

					Else
						If (FlxG._LastDrawingAlpha <> .5) Then
							SetAlpha(.5)
							FlxG._LastDrawingAlpha = .5
						End If
					End If

					DrawRect(bx, by, 8, i * 2)

					bx += 12
					by -= 2
					i += 1
				Wend

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.Mouse.Draw()

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And useSystemCursor) Then
			HideMouse()
		End If
		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play(FlxG.DATA_PREFIX + "beep")
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.CreateInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

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

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

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

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _UpdateSoundTray:Void()
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= FlxG.Elapsed

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (FlxG.Elapsed * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Update:Void()
		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()
		FlxG.UpdateCameras()
	End Method

	Method _Reset:Void()
		SetUpdateRate(FlxG.Framerate)
		Seed = SystemMillisecs()

		FlxG.DeviceWidth = DeviceWidth()
		FlxG.DeviceHeight = DeviceHeight()

		If (useVirtualResolution) Then
			FlxG._DeviceScaleFactorX = FlxG.DeviceWidth / Float(FlxG.Width)
			FlxG._DeviceScaleFactorY = FlxG.DeviceHeight / Float(FlxG.Height)
		Else
			FlxG._DeviceScaleFactorX = 1
			FlxG._DeviceScaleFactorY = 1
		End If

		_step = 1000.0 / FlxG.Framerate
		_lastUpdateTime = 0
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxG.DATA_PREFIX + FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) + ".png")
		Next

		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "default", FlxG.DATA_PREFIX + "default.png")
		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "button", FlxG.DATA_PREFIX + "button.png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor(FlxG.DATA_PREFIX + "cursor", FlxG.DATA_PREFIX + "cursor.png")
		FlxAssetsManager.AddSound(FlxG.DATA_PREFIX + "beep", FlxG.DATA_PREFIX + "beep.mp3")

		Self.OnContentInit()
	End Method
End Class