Added SetMouse for GLFW

Added SetMouse for GLFW

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

#if HOST="macos" And TARGET="glfw"
	Import "native/diddy.${TARGET}.mac.${LANG}"
#else
	Import "native/diddy.${TARGET}.${LANG}"
#end
Import mojo
Import assert

Extern

	#If LANG="cpp" Then
		Function RealMillisecs:Int() = "diddy::systemMillisecs"
		Function FlushKeys:Void() = "diddy::flushKeys"
		Function HideMouse:Void() = "diddy::hideMouse"
		Function ShowMouse:Void() = "diddy::showMouse"
		Function GetUpdateRate:Int() = "diddy::getUpdateRate"
		Function SetMouse:Void(x:Int, y:Int) = "diddy::setMouse"
	#Else
		Function RealMillisecs:Int() = "diddy.systemMillisecs"
		Function FlushKeys:Void() = "diddy.flushKeys"
		Function HideMouse:Void() = "diddy.hideMouse"
		Function ShowMouse:Void() = "diddy.showMouse"
		Function GetUpdateRate:Int() = "diddy.getUpdateRate"
		Function SetMouse:Void(x:Int, y:Int) = "diddy.setMouse"
	#End

Public

Function ExitApp:Void()
	Error ""
End

Function RectsOverlap:Int(x0:Float, y0:Float, w0:Float, h0:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
	If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
	Return True
End

Function DrawRectOutline:Void(x:Int, y:Int, w:Int, h:Int)
	w -= 1
	h -= 1
	DrawLine(x,y,x+w,y)
	DrawLine(x+w,y,x+w,y+h)
	DrawLine(x+w,y+h,x,y+h)
	DrawLine(x,y+h,x,y)
End

Function LoadBitmap:Image(path$, flags%=0)
	Local pointer:Image = LoadImage(path, 1, flags)

	AssertNotNull(pointer, "Error loading bitmap "+path)

   	Return pointer
End

Function LoadAnimBitmap:Image(path$, w%, h%, count%, tmpImage:Image)
	'tmpImage = loadBitmap(path) <-- This creates another image, decided to just copy the code here
	tmpImage = LoadImage(path)

	AssertNotNull(tmpImage, "Error loading bitmap "+path)

	Local pointer:Image = tmpImage.GrabImage( 0, 0, w, h, count, Image.MidHandle)

   	Return pointer
End

Function LoadSoundSample:Sound(path$)
	Local pointer:Sound = LoadSound(path)
	AssertNotNull(pointer, "Error loading sound "+path)
	Return pointer
End

Function FormatNumeric:String(value:Float)
	Local i:Int,s:String,ns:String,k:Int
	Local os:String
	s=String(value)
	os=s
	Local pos:Int=s.Length()
	If s.Find(".")>0 pos=s.Find(".") Else os=""
	For i=pos To 1 Step -1
		If k>2 ns+="." k=0
		k+=1
		ns=ns+Mid(s,i,1)
	Next
	s=""
	For i= ns.Length() To 1 Step -1
		s+=Mid(ns,i,1)
	Next
	If os<>"" s=s+","+os[pos+1..]
	Return s
End

Function Left$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[..n]
End

Function Right$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[str.Length()-n..]
End

Function LSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=str+rep
	Return str[..n]
End

Function RSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=rep+str
	Return str[str.Length()-n..]
End

Function Mid$( str$,pos:Int,size:Int=-1 )
	If pos>str.Length() Return ""
	pos-=1
	If( size<0 ) Return str[pos..]
	If pos<0 size=size+pos pos=0
	If pos+size>str.Length() size=str.Length()-pos
	Return str[pos..pos+size]
End

Function StripDir$( path$ )
	Local i:=path.FindLast( "/" )
	If i<>-1 Return path[i+1..]
	Return path
End

Function StripExt$( path$ )
	Local i:=path.FindLast( "." )
	If i<>-1 And path.Find( "/",i+1 )=-1 Return path[..i]
	Return path
End

Function StripAll$( path$ )
	Return StripDir( StripExt( path ) )
End

Function Round%(flot#)
	Return Floor(flot+0.5)
End

Function PointInSpot:Int(x1:Float, y1:Float, x2:Float, y2:Float, radius:Float)
	Local dx:Float = x2 - x1
	Local dy:Float = y2 - y1
	Return dx * dx + dy * dy <= radius * radius
End

Function AnyInputPressed:Bool()
	For Local i:Int = 0 To 511
		If KeyHit(i) Then Return True
	Next
	Return False
End

Function FormatNumber:String(number:Float, decimal:Int=4, comma:Int=0, padleft:Int=0 )
	Assert(decimal > -1 And comma > -1 And padleft > -1, "Negative numbers not allowed in FormatNumber()")

	Local str:String = number
	Local dl:Int = str.Find(".")
	If decimal = 0 Then decimal = -1
	str = str[..dl+decimal+1]

	If comma
		While dl>comma
			str = str[..dl-comma] + "," + str[dl-comma..]
			dl -= comma
		Wend
	End

	If padleft
		Local paddedLength:Int = padleft+decimal+1
		If paddedLength < str.Length Then str = "Error"
		str = RSet(str,paddedLength)
	End
	Return str
End

Function IsWhitespace:Bool(str:String)
	Return str = "~t" Or str = "~n" Or str = "~r" Or str = " "
End

Function IsWhitespace:Bool(val:Int)
	Return val = ASC_TAB Or val = ASC_LF Or val = ASC_CR Or val = ASC_SPACE
End

' control characters
Const ASC_NUL:Int = 0       ' Null character
Const ASC_SOH:Int = 1       ' Start of Heading
Const ASC_STX:Int = 2       ' Start of Text
Const ASC_ETX:Int = 3       ' End of Text
Const ASC_EOT:Int = 4       ' End of Transmission
Const ASC_ENQ:Int = 5       ' Enquiry
Const ASC_ACK:Int = 6       ' Acknowledgment
Const ASC_BEL:Int = 7       ' Bell
Const ASC_BACKSPACE:Int = 8 ' Backspace
Const ASC_TAB:Int = 9       ' Horizontal tab
Const ASC_LF:Int = 10       ' Linefeed
Const ASC_VTAB:Int = 11     ' Vertical tab
Const ASC_FF:Int = 12       ' Form feed
Const ASC_CR:Int = 13       ' Carriage return
Const ASC_SO:Int = 14       ' Shift Out
Const ASC_SI:Int = 15       ' Shift In
Const ASC_DLE:Int = 16      ' Data Line Escape
Const ASC_DC1:Int = 17      ' Device Control 1
Const ASC_DC2:Int = 18      ' Device Control 2
Const ASC_DC3:Int = 19      ' Device Control 3
Const ASC_DC4:Int = 20      ' Device Control 4
Const ASC_NAK:Int = 21      ' Negative Acknowledgment
Const ASC_SYN:Int = 22      ' Synchronous Idle
Const ASC_ETB:Int = 23      ' End of Transmit Block
Const ASC_CAN:Int = 24      ' Cancel
Const ASC_EM:Int = 25       ' End of Medium
Const ASC_SUB:Int = 26      ' Substitute
Const ASC_ESCAPE:Int = 27   ' Escape
Const ASC_FS:Int = 28       ' File separator
Const ASC_GS:Int = 29       ' Group separator
Const ASC_RS:Int = 30       ' Record separator
Const ASC_US:Int = 31       ' Unit separator

' visible characters
Const ASC_SPACE:Int = 32                ' '
Const ASC_EXCLAMATION:Int = 33          '!'
Const ASC_DOUBLE_QUOTE:Int = 34         '"'
Const ASC_HASH:Int = 35                 '#'
Const ASC_DOLLAR:Int = 36               '$'
Const ASC_PERCENT:Int = 37              '%'
Const ASC_AMPERSAND:Int = 38            '&'
Const ASC_SINGLE_QUOTE:Int = 39         '''
Const ASC_OPEN_PARENTHESIS:Int = 40     '('
Const ASC_CLOSE_PARENTHESIS:Int = 41    ')'
Const ASC_ASTERISK:Int = 42             '*'
Const ASC_PLUS:Int = 43                 '+'
Const ASC_COMMA:Int = 44                ','
Const ASC_HYPHEN:Int = 45               '-'
Const ASC_PERIOD:Int = 46               '.'
Const ASC_SLASH:Int = 47                '/'
Const ASC_0:Int = 48
Const ASC_1:Int = 49
Const ASC_2:Int = 50
Const ASC_3:Int = 51
Const ASC_4:Int = 52
Const ASC_5:Int = 53
Const ASC_6:Int = 54
Const ASC_7:Int = 55
Const ASC_8:Int = 56
Const ASC_9:Int = 57
Const ASC_COLON:Int = 58        ':'
Const ASC_SEMICOLON:Int = 59    ';'
Const ASC_LESS_THAN:Int = 60    '<'
Const ASC_EQUALS:Int = 61       '='
Const ASC_GREATER_THAN:Int = 62 '>'
Const ASC_QUESTION:Int = 63     '?'
Const ASC_AT:Int = 64           '@'
Const ASC_UPPER_A:Int = 65
Const ASC_UPPER_B:Int = 66
Const ASC_UPPER_C:Int = 67
Const ASC_UPPER_D:Int = 68
Const ASC_UPPER_E:Int = 69
Const ASC_UPPER_F:Int = 70
Const ASC_UPPER_G:Int = 71
Const ASC_UPPER_H:Int = 72
Const ASC_UPPER_I:Int = 73
Const ASC_UPPER_J:Int = 74
Const ASC_UPPER_K:Int = 75
Const ASC_UPPER_L:Int = 76
Const ASC_UPPER_M:Int = 77
Const ASC_UPPER_N:Int = 78
Const ASC_UPPER_O:Int = 79
Const ASC_UPPER_P:Int = 80
Const ASC_UPPER_Q:Int = 81
Const ASC_UPPER_R:Int = 82
Const ASC_UPPER_S:Int = 83
Const ASC_UPPER_T:Int = 84
Const ASC_UPPER_U:Int = 85
Const ASC_UPPER_V:Int = 86
Const ASC_UPPER_W:Int = 87
Const ASC_UPPER_X:Int = 88
Const ASC_UPPER_Y:Int = 89
Const ASC_UPPER_Z:Int = 90
Const ASC_OPEN_BRACKET:Int = 91     '['
Const ASC_BACKSLASH:Int = 92        '\'
Const ASC_CLOSE_BRACKET:Int = 93    ']'
Const ASC_CIRCUMFLEX:Int = 94       '^'
Const ASC_UNDERSCORE:Int = 95       '_'
Const ASC_BACKTICK:Int = 96         '`'
Const ASC_LOWER_A:Int = 97
Const ASC_LOWER_B:Int = 98
Const ASC_LOWER_C:Int = 99
Const ASC_LOWER_D:Int = 100
Const ASC_LOWER_E:Int = 101
Const ASC_LOWER_F:Int = 102
Const ASC_LOWER_G:Int = 103
Const ASC_LOWER_H:Int = 104
Const ASC_LOWER_I:Int = 105
Const ASC_LOWER_J:Int = 106
Const ASC_LOWER_K:Int = 107
Const ASC_LOWER_L:Int = 108
Const ASC_LOWER_M:Int = 109
Const ASC_LOWER_N:Int = 110
Const ASC_LOWER_O:Int = 111
Const ASC_LOWER_P:Int = 112
Const ASC_LOWER_Q:Int = 113
Const ASC_LOWER_R:Int = 114
Const ASC_LOWER_S:Int = 115
Const ASC_LOWER_T:Int = 116
Const ASC_LOWER_U:Int = 117
Const ASC_LOWER_V:Int = 118
Const ASC_LOWER_W:Int = 119
Const ASC_LOWER_X:Int = 120
Const ASC_LOWER_Y:Int = 121
Const ASC_LOWER_Z:Int = 122
Const ASC_OPEN_BRACE:Int = 123  '{'
Const ASC_PIPE:Int = 124        '|'
Const ASC_CLOSE_BRACE:Int = 125 '}'
Const ASC_TILDE:Int = 126       '~'
Const ASC_DELETE:Int = 127

#if HOST="macos" And TARGET="glfw"
	Import "native/diddy.${TARGET}.mac.${LANG}"
#else
	Import "native/diddy.${TARGET}.${LANG}"
#end
Import mojo
Import assert

Extern

	#If LANG="cpp" Then
		Function RealMillisecs:Int() = "diddy::systemMillisecs"
		Function FlushKeys:Void() = "diddy::flushKeys"
		Function HideMouse:Void() = "diddy::hideMouse"
		Function ShowMouse:Void() = "diddy::showMouse"
		Function GetUpdateRate:Int() = "diddy::getUpdateRate"
	#Else
		Function RealMillisecs:Int() = "diddy.systemMillisecs"
		Function FlushKeys:Void() = "diddy.flushKeys"
		Function HideMouse:Void() = "diddy.hideMouse"
		Function ShowMouse:Void() = "diddy.showMouse"
		Function GetUpdateRate:Int() = "diddy.getUpdateRate"
	#End

Public

Function ExitApp:Void()
	Error ""
End

Function RectsOverlap:Int(x0:Float, y0:Float, w0:Float, h0:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
	If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
	Return True
End

Function DrawRectOutline:Void(x:Int, y:Int, w:Int, h:Int)
	w -= 1
	h -= 1
	DrawLine(x,y,x+w,y)
	DrawLine(x+w,y,x+w,y+h)
	DrawLine(x+w,y+h,x,y+h)
	DrawLine(x,y+h,x,y)
End

Function LoadBitmap:Image(path$, flags%=0)
	Local pointer:Image = LoadImage(path, 1, flags)

	AssertNotNull(pointer, "Error loading bitmap "+path)

   	Return pointer
End

Function LoadAnimBitmap:Image(path$, w%, h%, count%, tmpImage:Image)
	'tmpImage = loadBitmap(path) <-- This creates another image, decided to just copy the code here
	tmpImage = LoadImage(path)

	AssertNotNull(tmpImage, "Error loading bitmap "+path)

	Local pointer:Image = tmpImage.GrabImage( 0, 0, w, h, count, Image.MidHandle)

   	Return pointer
End

Function LoadSoundSample:Sound(path$)
	Local pointer:Sound = LoadSound(path)
	AssertNotNull(pointer, "Error loading sound "+path)
	Return pointer
End

Function FormatNumeric:String(value:Float)
	Local i:Int,s:String,ns:String,k:Int
	Local os:String
	s=String(value)
	os=s
	Local pos:Int=s.Length()
	If s.Find(".")>0 pos=s.Find(".") Else os=""
	For i=pos To 1 Step -1
		If k>2 ns+="." k=0
		k+=1
		ns=ns+Mid(s,i,1)
	Next
	s=""
	For i= ns.Length() To 1 Step -1
		s+=Mid(ns,i,1)
	Next
	If os<>"" s=s+","+os[pos+1..]
	Return s
End

Function Left$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[..n]
End

Function Right$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[str.Length()-n..]
End

Function LSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=str+rep
	Return str[..n]
End

Function RSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=rep+str
	Return str[str.Length()-n..]
End

Function Mid$( str$,pos:Int,size:Int=-1 )
	If pos>str.Length() Return ""
	pos-=1
	If( size<0 ) Return str[pos..]
	If pos<0 size=size+pos pos=0
	If pos+size>str.Length() size=str.Length()-pos
	Return str[pos..pos+size]
End

Function StripDir$( path$ )
	Local i:=path.FindLast( "/" )
	If i<>-1 Return path[i+1..]
	Return path
End

Function StripExt$( path$ )
	Local i:=path.FindLast( "." )
	If i<>-1 And path.Find( "/",i+1 )=-1 Return path[..i]
	Return path
End

Function StripAll$( path$ )
	Return StripDir( StripExt( path ) )
End

Function Round%(flot#)
	Return Floor(flot+0.5)
End

Function PointInSpot:Int(x1:Float, y1:Float, x2:Float, y2:Float, radius:Float)
	Local dx:Float = x2 - x1
	Local dy:Float = y2 - y1
	Return dx * dx + dy * dy <= radius * radius
End

Function AnyInputPressed:Bool()
	For Local i:Int = 0 To 511
		If KeyHit(i) Then Return True
	Next
	Return False
End

Function FormatNumber:String(number:Float, decimal:Int=4, comma:Int=0, padleft:Int=0 )
	Assert(decimal > -1 And comma > -1 And padleft > -1, "Negative numbers not allowed in FormatNumber()")

	Local str:String = number
	Local dl:Int = str.Find(".")
	If decimal = 0 Then decimal = -1
	str = str[..dl+decimal+1]

	If comma
		While dl>comma
			str = str[..dl-comma] + "," + str[dl-comma..]
			dl -= comma
		Wend
	End

	If padleft
		Local paddedLength:Int = padleft+decimal+1
		If paddedLength < str.Length Then str = "Error"
		str = RSet(str,paddedLength)
	End
	Return str
End

Function IsWhitespace:Bool(str:String)
	Return str = "~t" Or str = "~n" Or str = "~r" Or str = " "
End

Function IsWhitespace:Bool(val:Int)
	Return val = ASC_TAB Or val = ASC_LF Or val = ASC_CR Or val = ASC_SPACE
End

' control characters
Const ASC_NUL:Int = 0       ' Null character
Const ASC_SOH:Int = 1       ' Start of Heading
Const ASC_STX:Int = 2       ' Start of Text
Const ASC_ETX:Int = 3       ' End of Text
Const ASC_EOT:Int = 4       ' End of Transmission
Const ASC_ENQ:Int = 5       ' Enquiry
Const ASC_ACK:Int = 6       ' Acknowledgment
Const ASC_BEL:Int = 7       ' Bell
Const ASC_BACKSPACE:Int = 8 ' Backspace
Const ASC_TAB:Int = 9       ' Horizontal tab
Const ASC_LF:Int = 10       ' Linefeed
Const ASC_VTAB:Int = 11     ' Vertical tab
Const ASC_FF:Int = 12       ' Form feed
Const ASC_CR:Int = 13       ' Carriage return
Const ASC_SO:Int = 14       ' Shift Out
Const ASC_SI:Int = 15       ' Shift In
Const ASC_DLE:Int = 16      ' Data Line Escape
Const ASC_DC1:Int = 17      ' Device Control 1
Const ASC_DC2:Int = 18      ' Device Control 2
Const ASC_DC3:Int = 19      ' Device Control 3
Const ASC_DC4:Int = 20      ' Device Control 4
Const ASC_NAK:Int = 21      ' Negative Acknowledgment
Const ASC_SYN:Int = 22      ' Synchronous Idle
Const ASC_ETB:Int = 23      ' End of Transmit Block
Const ASC_CAN:Int = 24      ' Cancel
Const ASC_EM:Int = 25       ' End of Medium
Const ASC_SUB:Int = 26      ' Substitute
Const ASC_ESCAPE:Int = 27   ' Escape
Const ASC_FS:Int = 28       ' File separator
Const ASC_GS:Int = 29       ' Group separator
Const ASC_RS:Int = 30       ' Record separator
Const ASC_US:Int = 31       ' Unit separator

' visible characters
Const ASC_SPACE:Int = 32                ' '
Const ASC_EXCLAMATION:Int = 33          '!'
Const ASC_DOUBLE_QUOTE:Int = 34         '"'
Const ASC_HASH:Int = 35                 '#'
Const ASC_DOLLAR:Int = 36               '$'
Const ASC_PERCENT:Int = 37              '%'
Const ASC_AMPERSAND:Int = 38            '&'
Const ASC_SINGLE_QUOTE:Int = 39         '''
Const ASC_OPEN_PARENTHESIS:Int = 40     '('
Const ASC_CLOSE_PARENTHESIS:Int = 41    ')'
Const ASC_ASTERISK:Int = 42             '*'
Const ASC_PLUS:Int = 43                 '+'
Const ASC_COMMA:Int = 44                ','
Const ASC_HYPHEN:Int = 45               '-'
Const ASC_PERIOD:Int = 46               '.'
Const ASC_SLASH:Int = 47                '/'
Const ASC_0:Int = 48
Const ASC_1:Int = 49
Const ASC_2:Int = 50
Const ASC_3:Int = 51
Const ASC_4:Int = 52
Const ASC_5:Int = 53
Const ASC_6:Int = 54
Const ASC_7:Int = 55
Const ASC_8:Int = 56
Const ASC_9:Int = 57
Const ASC_COLON:Int = 58        ':'
Const ASC_SEMICOLON:Int = 59    ';'
Const ASC_LESS_THAN:Int = 60    '<'
Const ASC_EQUALS:Int = 61       '='
Const ASC_GREATER_THAN:Int = 62 '>'
Const ASC_QUESTION:Int = 63     '?'
Const ASC_AT:Int = 64           '@'
Const ASC_UPPER_A:Int = 65
Const ASC_UPPER_B:Int = 66
Const ASC_UPPER_C:Int = 67
Const ASC_UPPER_D:Int = 68
Const ASC_UPPER_E:Int = 69
Const ASC_UPPER_F:Int = 70
Const ASC_UPPER_G:Int = 71
Const ASC_UPPER_H:Int = 72
Const ASC_UPPER_I:Int = 73
Const ASC_UPPER_J:Int = 74
Const ASC_UPPER_K:Int = 75
Const ASC_UPPER_L:Int = 76
Const ASC_UPPER_M:Int = 77
Const ASC_UPPER_N:Int = 78
Const ASC_UPPER_O:Int = 79
Const ASC_UPPER_P:Int = 80
Const ASC_UPPER_Q:Int = 81
Const ASC_UPPER_R:Int = 82
Const ASC_UPPER_S:Int = 83
Const ASC_UPPER_T:Int = 84
Const ASC_UPPER_U:Int = 85
Const ASC_UPPER_V:Int = 86
Const ASC_UPPER_W:Int = 87
Const ASC_UPPER_X:Int = 88
Const ASC_UPPER_Y:Int = 89
Const ASC_UPPER_Z:Int = 90
Const ASC_OPEN_BRACKET:Int = 91     '['
Const ASC_BACKSLASH:Int = 92        '\'
Const ASC_CLOSE_BRACKET:Int = 93    ']'
Const ASC_CIRCUMFLEX:Int = 94       '^'
Const ASC_UNDERSCORE:Int = 95       '_'
Const ASC_BACKTICK:Int = 96         '`'
Const ASC_LOWER_A:Int = 97
Const ASC_LOWER_B:Int = 98
Const ASC_LOWER_C:Int = 99
Const ASC_LOWER_D:Int = 100
Const ASC_LOWER_E:Int = 101
Const ASC_LOWER_F:Int = 102
Const ASC_LOWER_G:Int = 103
Const ASC_LOWER_H:Int = 104
Const ASC_LOWER_I:Int = 105
Const ASC_LOWER_J:Int = 106
Const ASC_LOWER_K:Int = 107
Const ASC_LOWER_L:Int = 108
Const ASC_LOWER_M:Int = 109
Const ASC_LOWER_N:Int = 110
Const ASC_LOWER_O:Int = 111
Const ASC_LOWER_P:Int = 112
Const ASC_LOWER_Q:Int = 113
Const ASC_LOWER_R:Int = 114
Const ASC_LOWER_S:Int = 115
Const ASC_LOWER_T:Int = 116
Const ASC_LOWER_U:Int = 117
Const ASC_LOWER_V:Int = 118
Const ASC_LOWER_W:Int = 119
Const ASC_LOWER_X:Int = 120
Const ASC_LOWER_Y:Int = 121
Const ASC_LOWER_Z:Int = 122
Const ASC_OPEN_BRACE:Int = 123  '{'
Const ASC_PIPE:Int = 124        '|'
Const ASC_CLOSE_BRACE:Int = 125 '}'
Const ASC_TILDE:Int = 126       '~'
Const ASC_DELETE:Int = 127


Small performance tweaks for group enumerator

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

Import reflection

Import flxextern
Import flxbasic
Import flxobject
Import flxu
Import system.flxarray

#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 _comparators:StringMap<Comparator>

	Field _comparator:Comparator

	Field _sortDescending:Bool

	Field _enumerator:Enumerator

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
		_comparators = New StringMap<Comparator>()
		_enumerator = New Enumerator(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

		If (_comparator <> Null) Then
			_comparator.Clear()
			_comparator = Null
		End If

		_comparators.Clear()
		_comparators = Null

		_enumerator.Destroy()
		_enumerator = 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)
		_comparator = _GetComparator(GetFirstNotNull().GetClassInfo().GetField(index))
		If (_comparator = Null) Return

		_sortDescending = order
		_QSort(0, _length - 1)
	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 _enumerator._Reset(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 _QSort:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSort(left, right, pivot)

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

	Method _DoSort: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 (basicToCompare = Null Or basic = Null) Then
				If (basicToCompare <> Null And basic = Null) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If

				i += 1
				Continue
			End If

			If (_sortDescending) Then
				If (_comparator.Compare(basicToCompare, basic) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (_comparator.Compare(basicToCompare, 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,[GetClass(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

	Method _GetComparator:Comparator(sortIndex:FieldInfo)
		If (sortIndex = Null) Return Null

		Local key:String = sortIndex.Type.Name

		If (sortIndex.Type.ExtendsClass(FlxU.GetObjectClass())) Then
			key = FlxU.GetObjectClass().Name
		End If

		Local comparator:Comparator = _comparators.Get(key)

		If (comparator = Null) Then
			Select key
				Case FloatClass().Name
					comparator = New FloatComparator()

				Case IntClass().Name
					comparator = New IntComparator()

				Case BoolClass().Name
					comparator = New BoolComparator()

				Case StringClass().Name
					comparator = New StringComparator()

				Default
					comparator = New ObjectComparator()

			End Select

			_comparators.Set(key, comparator)
		End If

		comparator.Clear()

		If (key <> FlxU.GetObjectClass().Name) Then
			comparator.Reset(sortIndex)
		Else
			Local sortMethod:MethodInfo = sortIndex.Type.GetMethod("Compare",[sortIndex.Type])
			If (sortMethod = Null) sortMethod = sortIndex.Type.GetMethod("Compare",[FlxU.GetObjectClass()])

			If (sortMethod <> Null) Then
				comparator.Reset(sortIndex, sortMethod)
			Else
				comparator = Null
			End If
		End If

		Return comparator
	End Method

End Class

Class Enumerator

	Method New(group:FlxGroup)
		_Reset(group)
	End

	Method HasNext:Bool()
		If (_dirty) Then
			_dirty = False
			_hasNext = False

			Repeat
				If (_nextIndex = _group._length) Then
					Return _hasNext
				End If

				_basic = _group._members[_nextIndex]

				_nextIndex += 1
			Until (_basic <> Null)

			_hasNext = True
		End If

		Return _hasNext
	End

	Method NextObject:FlxBasic()
		If (_dirty And Not HasNext()) Return Null

		_dirty = True
		Return _basic
	End

	Method Destroy:Void()
		_Reset(Null)
		_basic = Null
	End Method

Private

	Field _group:FlxGroup

	Field _index:Int

	Field _nextIndex:Int

	Field _hasNext:Bool

	Field _dirty:Bool

	Field _basic:FlxBasic

	Method _Reset:Enumerator(group:FlxGroup)
		_group = group
		_nextIndex = 0
		_hasNext = False
		_dirty = True

		Return Self
	End Method

End

Private

Class Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic) Abstract

	Method Reset:Comparator(sortIndex:FieldInfo, sortMethod:MethodInfo = Null)
		_sortIndex = sortIndex
		_sortMethod = sortMethod

		Return Self
	End Method

	Method Clear:Void()
		_sortIndex = Null
		_sortMethod = Null
	End Method

Private

	Field _sortIndex:FieldInfo

	Field _sortMethod:MethodInfo

End Class

Class BoolComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return Int(UnboxBool(_sortIndex.GetValue(basic1))) - Int(UnboxBool(_sortIndex.GetValue(basic2)))
	End Method

End Class

Class IntComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxInt(_sortIndex.GetValue(basic1)) - UnboxInt(_sortIndex.GetValue(basic2))
	End Method

End Class

Class FloatComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxFloat(_sortIndex.GetValue(basic1)) - UnboxFloat(_sortIndex.GetValue(basic2))
	End Method

End Class

Class StringComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxString(_sortIndex.GetValue(basic1)).Compare(UnboxString(_sortIndex.GetValue(basic2)))
	End Method

End Class

Class ObjectComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxInt(_sortMethod.Invoke(_sortIndex.GetValue(basic1),[_sortIndex.GetValue(basic2)]))
	End Method

End Class

#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 flxu
Import system.flxarray

#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 _comparators:StringMap<Comparator>

	Field _comparator:Comparator

	Field _sortDescending:Bool

	Field _enumerator:Enumerator

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
		_comparators = New StringMap<Comparator>()
		_enumerator = New Enumerator(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

		If (_comparator <> Null) Then
			_comparator.Clear()
			_comparator = Null
		End If

		_comparators.Clear()
		_comparators = Null

		_enumerator.Destroy()
		_enumerator = 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)
		_comparator = _GetComparator(GetFirstNotNull().GetClassInfo().GetField(index))
		If (_comparator = Null) Return

		_sortDescending = order
		_QSort(0, _length - 1)
	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 _enumerator._Reset(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 _QSort:Void(left:Int, right:Int)
		If (right > left) Then
			Local pivot:Int = left + (right-left)/2
			Local newPivot:Int = _DoSort(left, right, pivot)

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

	Method _DoSort: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 (basicToCompare = Null Or basic = Null) Then
				If (basicToCompare <> Null And basic = Null) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If

				i += 1
				Continue
			End If

			If (_sortDescending) Then
				If (_comparator.Compare(basicToCompare, basic) >= 0) Then
					_members[i] = _members[store]
					_members[store] = basicToCompare
					store+=1
				End If
			Else
				If (_comparator.Compare(basicToCompare, 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,[GetClass(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

	Method _GetComparator:Comparator(sortIndex:FieldInfo)
		If (sortIndex = Null) Return Null

		Local key:String = sortIndex.Type.Name

		If (sortIndex.Type.ExtendsClass(FlxU.GetObjectClass())) Then
			key = FlxU.GetObjectClass().Name
		End If

		Local comparator:Comparator = _comparators.Get(key)

		If (comparator = Null) Then
			Select key
				Case FloatClass().Name
					comparator = New FloatComparator()

				Case IntClass().Name
					comparator = New IntComparator()

				Case BoolClass().Name
					comparator = New BoolComparator()

				Case StringClass().Name
					comparator = New StringComparator()

				Default
					comparator = New ObjectComparator()

			End Select

			_comparators.Set(key, comparator)
		End If

		comparator.Clear()

		If (key <> FlxU.GetObjectClass().Name) Then
			comparator.Reset(sortIndex)
		Else
			Local sortMethod:MethodInfo = sortIndex.Type.GetMethod("Compare",[sortIndex.Type])
			If (sortMethod = Null) sortMethod = sortIndex.Type.GetMethod("Compare",[FlxU.GetObjectClass()])

			If (sortMethod <> Null) Then
				comparator.Reset(sortIndex, sortMethod)
			Else
				comparator = Null
			End If
		End If

		Return comparator
	End Method

End Class

Class Enumerator

	Method New(group:FlxGroup)
		_Reset(group)
	End

	Method HasNext:Bool()
		If (_dirty) Then
			_dirty = False
			_hasNext = False
			_nextIndex = _index

			Repeat
				If (_nextIndex = _group._length) Then
					Return _hasNext
				End If

				_basic = _group._members[_nextIndex]

				_nextIndex += 1
			Until (_basic <> Null)

			_hasNext = True
		End If

		Return _hasNext
	End

	Method NextObject:FlxBasic()
		If ( Not HasNext()) Return Null

		_dirty = True
		_index = _nextIndex
		Return _basic
	End

	Method Destroy:Void()
		_Reset(Null)
		_basic = Null
	End Method

Private

	Field _group:FlxGroup

	Field _index:Int

	Field _nextIndex:Int

	Field _hasNext:Bool

	Field _dirty:Bool

	Field _basic:FlxBasic

	Method _Reset:Enumerator(group:FlxGroup)
		_group = group
		_index = 0
		_nextIndex = 0
		_hasNext = False
		_dirty = True

		Return Self
	End Method

End

Private

Class Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic) Abstract

	Method Reset:Comparator(sortIndex:FieldInfo, sortMethod:MethodInfo = Null)
		_sortIndex = sortIndex
		_sortMethod = sortMethod

		Return Self
	End Method

	Method Clear:Void()
		_sortIndex = Null
		_sortMethod = Null
	End Method

Private

	Field _sortIndex:FieldInfo

	Field _sortMethod:MethodInfo

End Class

Class BoolComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return Int(UnboxBool(_sortIndex.GetValue(basic1))) - Int(UnboxBool(_sortIndex.GetValue(basic2)))
	End Method

End Class

Class IntComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxInt(_sortIndex.GetValue(basic1)) - UnboxInt(_sortIndex.GetValue(basic2))
	End Method

End Class

Class FloatComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxFloat(_sortIndex.GetValue(basic1)) - UnboxFloat(_sortIndex.GetValue(basic2))
	End Method

End Class

Class StringComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxString(_sortIndex.GetValue(basic1)).Compare(UnboxString(_sortIndex.GetValue(basic2)))
	End Method

End Class

Class ObjectComparator Extends Comparator

	Method Compare:Int(basic1:FlxBasic, basic2:FlxBasic)
		Return UnboxInt(_sortMethod.Invoke(_sortIndex.GetValue(basic1),[_sortIndex.GetValue(basic2)]))
	End Method

End Class

#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


Changed Format to accept strings since Monkey will convert primitives automatically.

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

Strict

Import functions

' Monkey converts primitives to strings when calling this function.
Function Format:String(fmt:String, arg1:String="", arg2:String="", arg3:String="", arg4:String="", arg5:String="", arg6:String="", arg7:String="", arg8:String="", arg9:String="", arg10:String="")
	Return FormatImpl(fmt, StringObject(arg1), StringObject(arg2), StringObject(arg3), StringObject(arg4), StringObject(arg5), StringObject(arg6), StringObject(arg7), StringObject(arg8), StringObject(arg9), StringObject(arg10))
End

Private

' This is a very simple implementation of sprintf.  I can't guarantee that it'll be bulletproof at this time, so use at your own risk!
' Note that due to Monkey's lack of varargs, Format accepts a maximum of 10 optional arguments.  Arguments are parsed up until the first Null.
' TODO: round floats rather than truncating them
Function FormatImpl:String(fmt:String, arg1:Object=Null, arg2:Object=Null, arg3:Object=Null, arg4:Object=Null, arg5:Object=Null, arg6:Object=Null, arg7:Object=Null, arg8:Object=Null, arg9:Object=Null, arg10:Object=Null)
	Local args:Object[] = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10]
	Local argcount:Int = 0
	For Local i:Int = 0 Until args.Length
		If args[i] = Null Or StringObject(args[i]) = "" Then
			argcount = i
			Exit
		End
	Next
	Local rv:String = ""
	Local formatting:Bool = False
	Local escapingBackslash:Bool = False
	Local escapingPercent:Bool = False
	Local ptr:Int = 0
	Local argnum:Int = 0
	While ptr < fmt.Length()
		Local chr:String = fmt[ptr..ptr+1]
		ptr+=1
		' if we're escaping with a backslash, just add the character
		If escapingBackslash Then
			rv += chr
			escapingBackslash = False

		' if we're not escaping but we received a backslash, just enable escaping
		ElseIf chr = "\" Then
			escapingBackslash = True

		' if we're not formatting and receive a %, enable formatting and escape percent
		ElseIf Not formatting And chr = "%" Then
			formatting = True
			escapingPercent = True

		' if we're escaping the percent and receive it, disable formatting
		ElseIf escapingPercent And chr = "%" Then
			rv += chr
			escapingPercent = False
			formatting = False

		' if we're not formatting, just add the character
		ElseIf Not formatting Then
			rv += chr

		' we're formatting
		Else
			' check that we have enough args
			If argnum >= argcount Then Error("Error: Didn't receive enough arguments in call to Format")
			Local fmtarg:String = chr
			Local foundPeriod:Bool = False, foundMinus:Bool = False, foundPadding:Bool = False
			Local formatLengthStr:String = "", formatLength:Int = 0, formatDPStr:String = "", formatDP:Int = 0
			Local formatType:String = ""
			' extract the rest of the format tag
			If Not IsValidFormat(chr) Then
				While ptr < fmt.Length()
					fmtarg += fmt[ptr..ptr+1]
					ptr+=1
					If IsValidFormat(fmtarg[fmtarg.Length-1..]) Then Exit
				End
			End
			' set format type
			formatType = fmtarg[fmtarg.Length-1..]

			' get the last character as the format type and die if it's wrong
			If formatType = "" Then
				' error!
				Error "Error parsing format string!"
			End

			Local fmtargptr:Int = 0
			' check for minus
			If fmtarg[0..1] = "-" Then
				foundMinus = True
				fmtargptr+=1
			' check for padding
			ElseIf fmtarg[fmtargptr..fmtargptr+1] = "0" Then
				foundPadding = True
				fmtargptr+=1
			End
			' check for digits up to a period or a character
			While fmtargptr < fmtarg.Length()
				If IsValidFormat(fmtargptr) Then
					Exit
				ElseIf fmtarg[fmtargptr] >= 48 And fmtarg[fmtargptr] <= 57 Then
					If Not foundPeriod Then
						formatLengthStr += fmtarg[fmtargptr..fmtargptr+1]
					Else
						formatDPStr += fmtarg[fmtargptr..fmtargptr+1]
					End
				ElseIf fmtarg[fmtargptr..fmtargptr+1] = "." Then
					foundPeriod = True
				End
				fmtargptr+=1
			End

			formatting = False
			If formatLengthStr <> "" Then formatLength = Int(formatLengthStr)
			If formatDPStr <> "" Then formatDP = Int(formatDPStr)

			If formatType = "d" Then
				Local ds:String = ArgToString(args[argnum])
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "f" Then
				Local df:Float = ArgToFloat(args[argnum])
				' TODO: round float instead of truncating
				Local ds:String = df
				Local dp:Int = ds.Find(".")
				If dp < 0 Then dp = ds.Length()
				Local whole:String = ds[..dp]
				Local part:String = "0"
				If dp < ds.Length()-1 Then part = ds[dp+1..]
				If part.Length() > formatDP Then part = part[..formatDP]
				While part.Length() < formatDP
					part+="0"
				End
				ds = whole
				If part.Length()>0 Then ds+="."+part
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "c" Then
				If foundPadding Or foundMinus Then Error "Error parsing format string!"
				rv += String.FromChar(ArgToInt(args[argnum]))
			ElseIf formatType = "s" Or formatType = "S" Then
				If foundPadding Then Error "Error parsing format string!"
				Local ds:String = ArgToString(args[argnum])
				If formatType = "S" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "x" Or formatType = "X" Then
				Local ds:String = DecToHex(ArgToInt(args[argnum])).ToLower()
				If formatType = "X" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			End
			argnum+=1
		End
	End
	Return rv
End

Function IsValidFormat:Bool(chr:String)
	Return chr = "d" Or chr = "f" Or chr = "s" Or chr = "S" Or chr = "c" Or chr = "x" Or chr = "X"
End

Function ArgToFloat:Float(arg:Object)
	If FloatObject(arg) Then Return FloatObject(arg)
	If IntObject(arg) Then Return IntObject(arg)
	If StringObject(arg) Then Return Float(StringObject(arg).value)
	Return 0
End

Function ArgToInt:Int(arg:Object)
	If FloatObject(arg) Then Return FloatObject(arg)
	If IntObject(arg) Then Return IntObject(arg)
	If StringObject(arg) Then Return Int(StringObject(arg).value)
	Return 0
End

Function ArgToString:String(arg:Object)
	If FloatObject(arg) Then Return String(FloatObject(arg).value)
	If IntObject(arg) Then Return String(IntObject(arg).value)
	If StringObject(arg) Then Return StringObject(arg)
	Return ""
End

Strict

Import functions

Function B:Object(v:Int)
	Return IntObject(v)
End

Function B:Object(v:Float)
	Return FloatObject(v)
End

Function B:Object(v:String)
	Return StringObject(v)
End

' This is a very simple implementation of sprintf.  I can't guarantee that it'll be bulletproof at this time, so use at your own risk!
' Monkey does not currently support autoboxing directly to Object, so unfortunately we need to manually do it.  Use the B() function for this.
' In other languages you might do the following:
'   sprintf(str, "%3d", 10); // C, C-like
'   str = String.format("%3d", 10); // Java
' But here you'll need to do this:
'   str = Format("%3d", B(10))
' Note that due to Monkey's lack of varargs, Format accepts a maximum of 10 optional arguments.  Arguments are parsed up until the first Null.
' TODO: round floats rather than truncating them
Function Format:String(fmt:String, arg1:Object=Null, arg2:Object=Null, arg3:Object=Null, arg4:Object=Null, arg5:Object=Null, arg6:Object=Null, arg7:Object=Null, arg8:Object=Null, arg9:Object=Null, arg10:Object=Null)
	Local args:Object[] = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10]
	Local argcount:Int = 0
	For Local i:Int = 0 Until args.Length
		If args[i] = Null Then
			argcount = i
			Exit
		End
	Next
	Local rv:String = ""
	Local formatting:Bool = False
	Local escapingBackslash:Bool = False
	Local escapingPercent:Bool = False
	Local ptr:Int = 0
	Local argnum:Int = 0
	While ptr < fmt.Length()
		Local chr:String = fmt[ptr..ptr+1]
		ptr+=1
		' if we're escaping with a backslash, just add the character
		If escapingBackslash Then
			rv += chr
			escapingBackslash = False

		' if we're not escaping but we received a backslash, just enable escaping
		ElseIf chr = "\" Then
			escapingBackslash = True

		' if we're not formatting and receive a %, enable formatting and escape percent
		ElseIf Not formatting And chr = "%" Then
			formatting = True
			escapingPercent = True

		' if we're escaping the percent and receive it, disable formatting
		ElseIf escapingPercent And chr = "%" Then
			rv += chr
			escapingPercent = False
			formatting = False

		' if we're not formatting, just add the character
		ElseIf Not formatting Then
			rv += chr

		' we're formatting
		Else
			' check that we have enough args
			If argnum >= argcount Then Error("Error: Didn't receive enough arguments in call to Format")
			Local fmtarg:String = chr
			Local foundPeriod:Bool = False, foundMinus:Bool = False, foundPadding:Bool = False
			Local formatLengthStr:String = "", formatLength:Int = 0, formatDPStr:String = "", formatDP:Int = 0
			Local formatType:String = ""
			' extract the rest of the format tag
			If Not IsValidFormat(chr) Then
				While ptr < fmt.Length()
					fmtarg += fmt[ptr..ptr+1]
					ptr+=1
					If IsValidFormat(fmtarg[fmtarg.Length-1..]) Then Exit
				End
			End
			' set format type
			formatType = fmtarg[fmtarg.Length-1..]

			' get the last character as the format type and die if it's wrong
			If formatType = "" Then
				' error!
				Error "Error parsing format string!"
			End

			Local fmtargptr:Int = 0
			' check for minus
			If fmtarg[0..1] = "-" Then
				foundMinus = True
				fmtargptr+=1
			' check for padding
			ElseIf fmtarg[fmtargptr..fmtargptr+1] = "0" Then
				foundPadding = True
				fmtargptr+=1
			End
			' check for digits up to a period or a character
			While fmtargptr < fmtarg.Length()
				If IsValidFormat(fmtargptr) Then
					Exit
				ElseIf fmtarg[fmtargptr] >= 48 And fmtarg[fmtargptr] <= 57 Then
					If Not foundPeriod Then
						formatLengthStr += fmtarg[fmtargptr..fmtargptr+1]
					Else
						formatDPStr += fmtarg[fmtargptr..fmtargptr+1]
					End
				ElseIf fmtarg[fmtargptr..fmtargptr+1] = "." Then
					foundPeriod = True
				End
				fmtargptr+=1
			End

			formatting = False
			If formatLengthStr <> "" Then formatLength = Int(formatLengthStr)
			If formatDPStr <> "" Then formatDP = Int(formatDPStr)

			If formatType = "d" Then
				Local ds:String = String(IntObject(args[argnum]))
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "f" Then
				Local df:Float = FloatObject(args[argnum])
				' TODO: round float instead of truncating
				Local ds:String = df
				Local dp:Int = ds.Find(".")
				If dp < 0 Then dp = ds.Length()
				Local whole:String = ds[..dp]
				Local part:String = "0"
				If dp < ds.Length()-1 Then part = ds[dp+1..]
				If part.Length() > formatDP Then part = part[..formatDP]
				While part.Length() < formatDP
					part+="0"
				End
				ds = whole
				If part.Length()>0 Then ds+="."+part
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "c" Then
				If foundPadding Or foundMinus Then Error "Error parsing format string!"
				rv += String.FromChar(IntObject(args[argnum]))
			ElseIf formatType = "s" Or formatType = "S" Then
				If foundPadding Then Error "Error parsing format string!"
				Local ds:String = StringObject(args[argnum])
				If formatType = "S" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "x" Or formatType = "X" Then
				Local ds:String = DecToHex(IntObject(args[argnum])).ToLower()
				If formatType = "X" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			End
			argnum+=1
		End
	End
	Return rv
End

Private

Function IsValidFormat:Bool(chr:String)
	Return chr = "d" Or chr = "f" Or chr = "s" Or chr = "S" Or chr = "c" Or chr = "x" Or chr = "X"
End


Removed an unnecessary import in 'base64'.

Strict

Public

' Imports:
Import brl.stream

' Constant variable(s) (Private):
Private

Const BASE64_CHAR_BOUNDS:= 64 ' __BASE64_CHARACTER_TABLE.Length

' DO NOT modify this "constant"; assume this as an internal constant variable.
Global __BASE64_CHARACTER_TABLE:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ' Const

Public

' Functions:

' This converts 'Value' into its original form.
Function Base64ToReal:Int(Value:Int)
	' Constant variable(s):

	' Exact ASCII symbols:
	Const ASCII_PLUS:= 43 ' +
	Const ASCII_SLASH:= 47 ' /
	Const ASCII_EQUALS:= 61 ' =

	' Segment lengths:
	Const ALPHABET_LENGTH:= 26
	Const NUMBER_LENGTH:= 10

	' "Virtual" array positions:
	Const UPPERCASE_OFFSET:= 0
	Const LOWERCASE_OFFSET:= (ALPHABET_LENGTH)
	Const NUMBER_OFFSET:= (ALPHABET_LENGTH*2)
	Const SPECIAL_OFFSET:= (NUMBER_OFFSET + (NUMBER_LENGTH-1))

	' ASCII/UTF8 character attributes:
	Const NUMBER_FIRST_CHAR:= 48 ' 0
	Const NUMBER_LAST_CHAR:= (NUMBER_OFFSET+(NUMBER_LENGTH-1)) ' 9

	Const UPPERCASE_FIRST_CHAR:= 65 ' A
	Const UPPERCASE_LAST_CHAR:= (UPPERCASE_FIRST_CHAR + (ALPHABET_LENGTH-1)) ' Z

	Const LOWERCASE_FIRST_CHAR:= 97 ' a
	Const LOWERCASE_LAST_CHAR:= (LOWERCASE_FIRST_CHAR + (ALPHABET_LENGTH-1)) ' z

	' The minimum character code required to begin parsing.
	Const CHAR_ENTRYPOINT:= NUMBER_FIRST_CHAR
	Const CHAR_ALPH_ENTRYPOINT:= UPPERCASE_FIRST_CHAR

	' Check if this is a supported character:
	If (Value >= CHAR_ENTRYPOINT) Then
		' Check if this is an extended or alphabetical character:
		If (Value >= CHAR_ALPH_ENTRYPOINT) Then
			' Check for uppercase characters:
			If (Value <= UPPERCASE_LAST_CHAR) Then
				' Convert 'Value' as an uppercase character.
				Return (Value - UPPERCASE_FIRST_CHAR) + UPPERCASE_OFFSET
			Endif

			' Check for lowercase characters:
			If (Value >= LOWERCASE_FIRST_CHAR And Value <= LOWERCASE_LAST_CHAR) Then
				' Convert 'Value' as a lowercase character.
				Return (Value - LOWERCASE_FIRST_CHAR) + LOWERCASE_OFFSET
			Endif

			' Check if this is a "special" character, and if so, convert it:
			Select Value
				Case ASCII_PLUS
					Return SPECIAL_OFFSET
				Case ASCII_SLASH
					Return SPECIAL_OFFSET+1
				Case ASCII_EQUALS
					Return SPECIAL_OFFSET+2
			End Select
		Else
			' Check if this is a numeric character:
			If (Value < = NUMBER_LAST_CHAR) Then
				' Convert 'Value' as a number.
				Return (Value - NUMBER_FIRST_CHAR) + NUMBER_OFFSET
			Endif
		Endif
	Endif

	' Return the default response. (Unsupported character)
	Return -1
End

' This encodes 'Data' into raw base 64 text; based off of Diddy's implementation.
Function EncodeBase64:Void(Data:Stream, Length:Int, Output:Stream)
	' Local variable(s):
	Local A:Int, B:Int, C:Int, D:Int

	For Local I:= 0 Until Length Step 3
		Local S1:Int

		' Read the initial byte, then resolve the next two for this cycle:
		S1 = (Data.ReadByte() & $FF)

		' Assign our byte representatives:
		A = S1 Shr 2
		B = ((S1 & 3) Shl 4)

		' Load our first unresolved byte
		If (I+1 < Length) Then
			Local S2:= (Data.ReadByte() & $FF)

			B |= (S2 Shr 4)
			C = ((S2 & 15) Shl 2)
		Else
			C = BASE64_CHAR_BOUNDS
		Endif

		' Load the second unresolved byte:
		If (I+2 < Length) Then
			Local S3:= (Data.ReadByte() & $FF)

			C |= (S3 Shr 6)
			D = (S3 & 63)
		Else
			D = BASE64_CHAR_BOUNDS
		Endif

		Output.WriteByte(__BASE64_CHARACTER_TABLE[A])
		Output.WriteByte(__BASE64_CHARACTER_TABLE[B])

		' A bit of an order-hack, but it works:
		If (C <= BASE64_CHAR_BOUNDS) Then
			Output.WriteByte(__BASE64_CHARACTER_TABLE[C])
		Endif

		If (D <= BASE64_CHAR_BOUNDS) Then
			Output.WriteByte(__BASE64_CHARACTER_TABLE[D])
		Endif
	Next

	Return
End

Function EncodeBase64:Void(Data:Stream, Output:Stream)
	EncodeBase64(Data, Data.Length, Output)

	Return
End

' This decodes raw base 64 text from 'Data' into 'Output'; based off of Diddy's implementation.
Function DecodeBase64:Void(Data:Stream, Length:Int, Output:Stream)
	For Local I:= 0 Until Length Step 4
		Local A:Int, B:Int, C:Int, D:Int

		A = Base64ToReal(Data.ReadByte())

		If (I+1 > Length) Then
			' Throw ...

			Exit
		Endif

		B = Base64ToReal(Data.ReadByte())

		If (I+2 < Length) Then
			C = Base64ToReal(Data.ReadByte())
		Else
			C = BASE64_CHAR_BOUNDS
		Endif

		If (I+3 < Length) Then
			D = Base64ToReal(Data.ReadByte())
		Else
			D = BASE64_CHAR_BOUNDS
		Endif

		Output.WriteByte((A Shl 2) | (B Shr 4))
		Output.WriteByte(((B & 15) Shl 4) | (C Shr 2))
		Output.WriteByte(((C & 3) Shl 6) | D)
	Next

	Return
End

Function DecodeBase64:Void(Data:Stream, Output:Stream)
	DecodeBase64(Data, Data.Length, Output)

	Return
End

Strict

Public

' Imports (Public):
Import brl.stream

' Imports (Private):
Private

Import regal.typetool

Public

' Constant variable(s) (Private):
Private

Const BASE64_CHAR_BOUNDS:= 64 ' __BASE64_CHARACTER_TABLE.Length

' DO NOT modify this "constant"; assume this as an internal constant variable.
Global __BASE64_CHARACTER_TABLE:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ' Const

Public

' Functions:

' This converts 'Value' into its original form.
Function Base64ToReal:Int(Value:Int)
	' Constant variable(s):

	' Exact ASCII symbols:
	Const ASCII_PLUS:= 43 ' +
	Const ASCII_SLASH:= 47 ' /
	Const ASCII_EQUALS:= 61 ' =

	' Segment lengths:
	Const ALPHABET_LENGTH:= 26
	Const NUMBER_LENGTH:= 10

	' "Virtual" array positions:
	Const UPPERCASE_OFFSET:= 0
	Const LOWERCASE_OFFSET:= (ALPHABET_LENGTH)
	Const NUMBER_OFFSET:= (ALPHABET_LENGTH*2)
	Const SPECIAL_OFFSET:= (NUMBER_OFFSET + (NUMBER_LENGTH-1))

	' ASCII/UTF8 character attributes:
	Const NUMBER_FIRST_CHAR:= 48 ' 0
	Const NUMBER_LAST_CHAR:= (NUMBER_OFFSET+(NUMBER_LENGTH-1)) ' 9

	Const UPPERCASE_FIRST_CHAR:= 65 ' A
	Const UPPERCASE_LAST_CHAR:= (UPPERCASE_FIRST_CHAR + (ALPHABET_LENGTH-1)) ' Z

	Const LOWERCASE_FIRST_CHAR:= 97 ' a
	Const LOWERCASE_LAST_CHAR:= (LOWERCASE_FIRST_CHAR + (ALPHABET_LENGTH-1)) ' z

	' The minimum character code required to begin parsing.
	Const CHAR_ENTRYPOINT:= NUMBER_FIRST_CHAR
	Const CHAR_ALPH_ENTRYPOINT:= UPPERCASE_FIRST_CHAR

	' Check if this is a supported character:
	If (Value >= CHAR_ENTRYPOINT) Then
		' Check if this is an extended or alphabetical character:
		If (Value >= CHAR_ALPH_ENTRYPOINT) Then
			' Check for uppercase characters:
			If (Value <= UPPERCASE_LAST_CHAR) Then
				' Convert 'Value' as an uppercase character.
				Return (Value - UPPERCASE_FIRST_CHAR) + UPPERCASE_OFFSET
			Endif

			' Check for lowercase characters:
			If (Value >= LOWERCASE_FIRST_CHAR And Value <= LOWERCASE_LAST_CHAR) Then
				' Convert 'Value' as a lowercase character.
				Return (Value - LOWERCASE_FIRST_CHAR) + LOWERCASE_OFFSET
			Endif

			' Check if this is a "special" character, and if so, convert it:
			Select Value
				Case ASCII_PLUS
					Return SPECIAL_OFFSET
				Case ASCII_SLASH
					Return SPECIAL_OFFSET+1
				Case ASCII_EQUALS
					Return SPECIAL_OFFSET+2
			End Select
		Else
			' Check if this is a numeric character:
			If (Value < = NUMBER_LAST_CHAR) Then
				' Convert 'Value' as a number.
				Return (Value - NUMBER_FIRST_CHAR) + NUMBER_OFFSET
			Endif
		Endif
	Endif

	' Return the default response. (Unsupported character)
	Return -1
End

' This encodes 'Data' into raw base 64 text; based off of Diddy's implementation.
Function EncodeBase64:Void(Data:Stream, Length:Int, Output:Stream)
	' Local variable(s):
	Local A:Int, B:Int, C:Int, D:Int

	For Local I:= 0 Until Length Step 3
		Local S1:Int

		' Read the initial byte, then resolve the next two for this cycle:
		S1 = (Data.ReadByte() & $FF)

		' Assign our byte representatives:
		A = S1 Shr 2
		B = ((S1 & 3) Shl 4)

		' Load our first unresolved byte
		If (I+1 < Length) Then
			Local S2:= (Data.ReadByte() & $FF)

			B |= (S2 Shr 4)
			C = ((S2 & 15) Shl 2)
		Else
			C = BASE64_CHAR_BOUNDS
		Endif

		' Load the second unresolved byte:
		If (I+2 < Length) Then
			Local S3:= (Data.ReadByte() & $FF)

			C |= (S3 Shr 6)
			D = (S3 & 63)
		Else
			D = BASE64_CHAR_BOUNDS
		Endif

		Output.WriteByte(__BASE64_CHARACTER_TABLE[A])
		Output.WriteByte(__BASE64_CHARACTER_TABLE[B])

		' A bit of an order-hack, but it works:
		If (C <= BASE64_CHAR_BOUNDS) Then
			Output.WriteByte(__BASE64_CHARACTER_TABLE[C])
		Endif

		If (D <= BASE64_CHAR_BOUNDS) Then
			Output.WriteByte(__BASE64_CHARACTER_TABLE[D])
		Endif
	Next

	Return
End

Function EncodeBase64:Void(Data:Stream, Output:Stream)
	EncodeBase64(Data, Data.Length, Output)

	Return
End

' This decodes raw base 64 text from 'Data' into 'Output'; based off of Diddy's implementation.
Function DecodeBase64:Void(Data:Stream, Length:Int, Output:Stream)
	For Local I:= 0 Until Length Step 4
		Local A:Int, B:Int, C:Int, D:Int

		A = Base64ToReal(Data.ReadByte())

		If (I+1 > Length) Then
			' Throw ...

			Exit
		Endif

		B = Base64ToReal(Data.ReadByte())

		If (I+2 < Length) Then
			C = Base64ToReal(Data.ReadByte())
		Else
			C = BASE64_CHAR_BOUNDS
		Endif

		If (I+3 < Length) Then
			D = Base64ToReal(Data.ReadByte())
		Else
			D = BASE64_CHAR_BOUNDS
		Endif

		Output.WriteByte((A Shl 2) | (B Shr 4))
		Output.WriteByte(((B & 15) Shl 4) | (C Shr 2))
		Output.WriteByte(((C & 3) Shl 6) | D)
	Next

	Return
End

Function DecodeBase64:Void(Data:Stream, Output:Stream)
	DecodeBase64(Data, Data.Length, Output)

	Return
End