New line char fixed

New line char fixed

Strict

Import flxextern
Import flxsprite
Import flxcamera
Import flxg
Import system.flxcolor
Import system.flxassetsmanager

Private

#If FLX_TEXT_DRIVER = "angelfont"
	Import vendor.angelfont
#ElseIf FLX_TEXT_DRIVER = "fontmachine"
	Import vendor.fontmachine
#End

Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_8_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_8_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_9_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_9_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_10_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_10_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_11_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_11_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_12_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_12_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_13_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_13_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_14_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_14_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_15_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_15_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_16_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_16_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_17_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_17_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_18_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_18_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_19_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_19_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_20_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_20_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_21_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_21_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_22_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_22_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_23_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_23_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_24_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_24_flx.${FLX_FONT_EXTENSION}"

Public

Class FlxText Extends FlxSprite Implements FlxSpriteRenderer

	Global ClassObject:Object

	Const ALIGN_LEFT:Float = 0
	Const ALIGN_CENTER:Float = 0.5
	Const ALIGN_RIGHT:Float = 1

	Const SYSTEM_FONT:String = "system"

Private
	Global _FontLoader:FlxFontLoader = New FlxFontLoader()

	Global _FontsManager:FlxResourcesManager<FlxBitmapFont> = New FlxResourcesManager<FlxBitmapFont>()

	Field _shadow:FlxColor

	Field _value:String

	Field _lines:FlxTextLine[]

	Field _countLines:Int

	Field _alignment:Float

	Field _fontFamily:String

	Field _fontSize:Int

	Field _fontHeight:Int

	Field _fontObject:FlxBitmapFont

Public
	Method New(x:Float, y:Float, width:Int = 0, text:String = "")
		Super.New(x, y)

		SetRenderer(Self)
		_shadow = New FlxColor(0)

		Self.width = width
		frameWidth = Self.width
		moves = False

		SetFormat(SYSTEM_FONT)
		Text = text
	End Method

	Method Destroy:Void()
		Local i:Int = 0, l:Int = _lines.Length()

		While (i < l)
			_lines[i] = Null
			i += 1
		Wend

		_fontObject = Null
		_shadow = Null

		Super.Destroy()
	End Method

	Method SetFormat:FlxText(font:String = "", size:Int = 0, color:Int = FlxG.WHITE, alignment:Float = ALIGN_LEFT, shadowColor:Int = 0)
		_alignment = alignment
		_fontFamily = font
		_fontSize = FlxAssetsManager.GetFont(font).GetValidSize(size)

		Self.Color = color
		Shadow = shadowColor

		_ResetFont()
		_ResetHelpers()

		Return Self
	End Method

	Method SetWidth:Void(width:Float)
		Self.width = width
		frameWidth = Self.width
		_ResetHelpers()
	End Method

	Method Text:String() Property
		Return _value
	End Method

	Method Text:Void(text:String) Property
		_value = text
		_ResetHelpers()
	End Method

	Method Size:Void(size:Int) Property
		_fontSize = FlxAssetsManager.GetFont(_fontFamily).GetValidSize(size)
		_ResetFont()
		_ResetHelpers()
	End Method

	Method Size:Int() Property
		Return _fontSize
	End Method

	Method Font:String() Property
		Return _fontFamily
	End Method

	Method Font:Void(font:String) Property
		_fontFamily = font
		_ResetFont()
		_ResetHelpers()
	End Method

	Method Alignment:Float() Property
		Return _alignment
	End Method

	Method Alignment:Void(alignment:Float) Property
		If (_alignment = alignment) Return
		_alignment = alignment
		_ResetAlignment()
	End Method

	Method Shadow:Int() Property
		Return _shadow.argb
	End Method

	Method Shadow:Void(color:Int) Property
		_shadow.SetARGB(color)
	End Method

	Method SetFontShadowEnabled:Void(enabled:Bool)
	#If FLX_TEXT_DRIVER = "angelfont"
		Return
	#ElseIf FLX_TEXT_DRIVER = "fontmachine"
		_fontObject._drawShadow = enabled
	#End
	End Method

	Method SetFontBoderEnabled:Void(enabled:Bool)
	#If FLX_TEXT_DRIVER = "angelfont"
		Return
	#ElseIf FLX_TEXT_DRIVER = "fontmachine"
		_fontObject._drawBorder = enabled
	#End
	End Method

	Method OnSpriteRender:Void(x:Float, y:Float)
		If (_shadow.argb <> 0) Then
			Local oldColor:Int = FlxG._LastDrawingColor
			Local oldAlpha:Int = FlxG._LastDrawingAlpha

			If (_camera.Color <> FlxG.WHITE) Then
				_mixedColor.MixRGB(_shadow, _camera._color)

				If (FlxG._LastDrawingColor <> _mixedColor.argb) Then
					SetColor(_mixedColor.r, _mixedColor.g, _mixedColor.b)
				End If
			Else
				If (FlxG._LastDrawingColor <> _shadow.argb) Then
					SetColor(_shadow.r, _shadow.g, _shadow.b)
				End If
			End If

			If (_camera.Alpha < 1) Then
				Local _mixedAlpha:Float = _camera.Alpha * _shadow.a

				If (FlxG._LastDrawingAlpha <> _mixedAlpha) Then
					SetAlpha(_mixedAlpha)
				End If
			Else
				If (FlxG._LastDrawingAlpha <> _shadow.a) Then
					SetAlpha(_shadow.a)
				End If
			End If

			_fontObject.DrawText(Self, x + 1, y + 1)

			_mixedColor.SetRGB(oldColor)
			SetColor(_mixedColor.r, _mixedColor.g, _mixedColor.b)
			SetAlpha(oldAlpha)
		End If

		_fontObject.DrawText(Self, x, y)
	End Method

Private
	Method _ResetHelpers:Void()
		If (_value.Length() > 0) Then
			If (width <> 0) Then
				_ParseText()
			Else
				_countLines = 0
				_AddLine(0, _value.Length(), _fontObject.GetTextWidth(Self, 0, _value.Length()))
				_ResetAlignment()
			End If

			Self.height = _GetHeight()
			frameHeight = Self.height
		Else
			Self.height = 0
			frameHeight = 0
		End If

		Super._ResetHelpers()
	End Method

	Method _ResetFont:Void()
		_FontLoader.fontFamily = _fontFamily
		_FontLoader.fontSize = _fontSize

		_fontObject = _FontsManager.GetResource(_fontFamily + _fontSize, _FontLoader)
		_fontHeight = _fontObject.GetFontHeight(Self)
	End Method

	Method _ResetAlignment:Void()
		If (_value.Length() = 0) Return

		For Local line:Int = 0 Until _countLines
			_lines[line].xOffset = (width - _lines[line].width) * _alignment
		Next
	End Method

	Method _GetHeight:Int()
		If (width = 0) Then
			Return _fontHeight
		Else
			Return _fontObject.GetTextHeight(Self)
		End If
	End Method

	Method _ParseText:Void()
		_countLines = 0

		Local prevOffset:Int = 0
		Local offset:Int = -1

		Local i:Int = 0, l:Int = _value.Length()

		While (i < l)
			If (_value[i] = 10 Or _value[i] = KEY_ENTER) Then
				offset = i
				Exit
			End If

			i += 1
		Wend

		If (offset >= 0) Then
			While (offset >= 0)
				_BuildLines(prevOffset, offset)

				prevOffset = offset + 1

				i = prevOffset
				offset = -1

				While (i < l)
					If (_value[i] = 10 Or _value[i] = KEY_ENTER) Then
						offset = i
						Exit
					End If

					i += 1
				Wend
			Wend

			_BuildLines(prevOffset, _value.Length())
		Else
			_BuildLines(0, _value.Length())
		End If

		_ResetAlignment()
	End Method

	Method _BuildLines:Void(startPos:Int, endPos:Int)
		Local textWidth:Int = _fontObject.GetTextWidth(Self, startPos, endPos)

		If (width < textWidth) Then
			Local textLength:Int = endPos - startPos

			Local range:Int = Ceil(textLength / Float(Floor(textWidth / Float(width)) + 1))

			Repeat
				range += 1
				textWidth = _fontObject.GetTextWidth(Self, 0, range)
			Until (textWidth >= width)

			Local maxOffset:Int = range
			Local minOffset:Int = 0
			Local offset:Int = maxOffset
			Local tmpOffset:Int = 0
			Local dirty:Bool = False
			Local finalTextWidth:Int = 0
			Local success:Bool

			Repeat
				Repeat
					offset -= 1
					If (offset - minOffset <= 1) Then
						offset = minOffset + 1
						Exit
					End if

					tmpOffset = -1
					For Local i:Int = offset - 1 To minOffset Step - 1
						success = False

						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							tmpOffset = i
							success = True
							Exit
						End If
					Next

					If (tmpOffset < 0) Then
						tmpOffset = _GetMinOffset(minOffset, offset)
					Else
						If (_value[minOffset] = KEY_SPACE Or _value[minOffset] = KEY_TAB) Then
							For Local i:Int = minOffset To offset
								If (_value[i] <> KEY_SPACE And _value[i] <> KEY_TAB) Then
									minOffset = i
									Exit
								End If
							Next
						End If
					End If

					offset = tmpOffset
					textWidth = _fontObject.GetTextWidth(Self, minOffset, offset)
				Until (textWidth <= width)

				dirty = False
				finalTextWidth = _fontObject.GetTextWidth(Self, minOffset)

				If (finalTextWidth > width And textLength - minOffset > 1) Then
					For Local i:Int = minOffset Until offset
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							minOffset += 1
							dirty = True
						Else
							Exit
						End If
					Next

					For Local i:Int = offset - 1 To minOffset Step - 1
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							offset -= 1
							dirty = True
						Else
							Exit
						End If
					Next

					If (offset - minOffset = 0) Continue

					If ( Not dirty) Then
						_AddLine(minOffset, offset, textWidth)
					Else
						_AddLine(minOffset, offset, _fontObject.GetTextWidth(Self, minOffset, offset))
					End If

					minOffset = offset
					If (success) minOffset += 1

					maxOffset = minOffset + range
					offset = maxOffset
				Else
					Local l:Int = _value.Length()

					For Local i:Int = minOffset Until l
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							minOffset += 1
							dirty = True
						Else
							Exit
						End If
					Next

					For Local i:Int = l - 1 To minOffset Step - 1
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							l -= 1
							dirty = True
						Else
							Exit
						End If
					Next

					If (l - minOffset <= 0) Exit

					If ( Not dirty) Then
						_AddLine(minOffset, l, finalTextWidth)
					Else
						_AddLine(minOffset, offset, _fontObject.GetTextWidth(Self, minOffset, l))
					End If

					Exit
				End If
			Forever
		Else
			_AddLine(startPos, endPos, textWidth)
		End If
	End Method

	Method _GetMinOffset:Int(startPos:Int, endPos:Int)
		Local offset:Int = endPos - startPos

		While (_fontObject.GetTextWidth(Self, startPos, startPos + offset) > width)
			offset -= 1
			If (offset = 1) Return offset
		Wend

		Return startPos + offset
	End Method

	Method _AddLine:FlxTextLine(startPos:Int, endPos:Int, width:Int)
		Local line:FlxTextLine

		If (_countLines = _lines.Length()) Then
			_lines = _lines.Resize(_countLines * 2 + 10)
			line = New FlxTextLine(startPos, endPos, width)
			_lines[_countLines] = line
		Else
			line = _lines[_countLines]
			If (line = Null) Then
				line = New FlxTextLine(startPos, endPos, width)
				_lines[_countLines] = line
			Else
				line.Reset(startPos, endPos, width)
			End If
		End If

		_countLines += 1
		Return line
	End Method

End Class

Private
Class FlxTextLine

	Field xOffset:Float

	Field startPos:Int

	Field endPos:Int

	Field width:Int

	Method New(startPos:Int, endPos:Int, width:Int)
		Reset(startPos, endPos, width)
	End Method

	Method Reset:Void(startPos:Int, endPos:Int, width:Int)
		Self.startPos = startPos
		Self.endPos = endPos
		Self.width = width
	End Method

End Class

Class FlxFontLoader Extends FlxResourceLoader<FlxBitmapFont>

	Field fontFamily:String = FlxText.SYSTEM_FONT
	Field fontSize:Int

	Method Load:FlxBitmapFont(name:String)
		Return New FlxBitmapFont(FlxAssetsManager.GetFont(fontFamily).GetPath(fontSize))
	End Method

End Class

#If FLX_TEXT_DRIVER = "angelfont"
	Class FlxBitmapFont
	Private
		Field image:Image[] = New Image[1]
		Field chars:Char[256]

		Field kernPairs:IntMap<IntMap<KernPair>> = New IntMap<IntMap<KernPair>>
		Global firstKp:IntMap<KernPair>
		Global secondKp:KernPair

		Field iniText:String

		Field xOffset:Int
		Field yOffset:Int

	Public
		Field useKerning:Bool = True

		Field height:Int = 0
		Field heightOffset:Int = 9999

		Field lineGap:Int = 5

		Method New(url:String)
			iniText = LoadString(url+".fnt")
			Local lines:String[] = iniText.Split(String.FromChar(10))
			Local firstLine:String = lines[0]
			If firstLine.Contains("<?xml")
				Local lineList:List<String> = New List<String>(lines)
				lineList.RemoveFirst()
				lines = lineList.ToArray()
				iniText = "~n".Join(lines)
			End

			Local pageCount:Int = 0

			Local config:= LoadConfig(iniText)

			Local nodes:= config.FindNodesByPath("font/chars/char")

			For Local node:= EachIn nodes
				Local id:Int = Int(node.GetAttribute("id"))
				Local page:Int = Int(node.GetAttribute("page"))
				If pageCount < page pageCount = page
				chars[id] = New Char(Int(node.GetAttribute("x")), Int(node.GetAttribute("y")), Int(node.GetAttribute("width")), Int(node.GetAttribute("height")),  Int(node.GetAttribute("xoffset")),  Int(node.GetAttribute("yoffset")),  Int(node.GetAttribute("xadvance")), page)
				Local ch := chars[id]
				If ch.height > Self.height Self.height = ch.height
				If ch.yOffset < Self.heightOffset Self.heightOffset = ch.yOffset
			Next

			nodes = config.FindNodesByPath("font/kernings/kerning")

			If (nodes <> Null) Then
				For Local node:= EachIn nodes
					Local first:Int = Int(node.GetAttribute("first"))
					firstKp = kernPairs.Get(first)
					If firstKp = Null
						kernPairs.Add(first, New IntMap<KernPair>)
						firstKp = kernPairs.Get(first)
					End

					Local second:Int = Int(node.GetAttribute("second"))
					firstKp.Add(second, New KernPair(first, second, Int(node.GetAttribute("amount"))))
				End
			Else
				useKerning = False
			End If

			If pageCount = 0
				image[0] = LoadImage(url+".png")
				If image[0] = Null image[0] = LoadImage(url + "_0.png")
			Else
				For Local page:= 0 To pageCount
					If image.Length < page+1 image = image.Resize(page+1)
					image[page] = LoadImage(url+"_"+page+".png")
				End
			End
		End

		Method GetTextWidth:Int(txt:FlxText, startPos:Int = 0, endPos:Int = -1)
			Local prevChar:Int = 0
			Local width:Int = 0

			If (endPos < 0) endPos = txt._value.Length()
			Local asc:Int, ac:Char

			For Local i:= startPos Until endPos
				asc = txt._value[i]
				ac = chars[asc]

				If ac <> Null
					If useKerning
						Local firstKp:= kernPairs.Get(prevChar)
						If firstKp <> Null
							Local secondKp:= firstKp.Get(asc)
							If secondKp <> Null
								xOffset += secondKp.amount
							End
						Endif
					EndIf

					width += ac.xAdvance
					prevChar = asc
				EndIf
			Next

			Return width
		End Method

		Method GetFontHeight:Int(txt:FlxText)
			Local h:Int = 0

			If (txt._value.Length() <> 0) Then
				Local l:Int = txt._value.Length()

				For Local i:= 0 Until l
					Local asc:Int = txt._value[i]
					Local ac:Char = chars[asc]
					If ac <> Null And ac.height + ac.yOffset > h h = ac.height + ac.yOffset
				Next
			Else
				h = chars[KEY_SPACE].height + chars[KEY_SPACE].yOffset
			End If

			Return h
		End

		Method GetTextHeight:Int(txt:FlxText)
			Return ( (txt._fontHeight + lineGap) * txt._countLines) - lineGap
		End Method

		Method DrawText:Void(txt:FlxText, x:Int, y:Int)
			Local lineIndex:Int = 0, countLines:Int = txt._countLines, line:FlxTextLine

			Local prevChar:Int = 0, xOffset:Int = 0, yOffset:Int = 0
			Local i:Int = 0, l:Int
			Local asc:Int, ac:Char
			Local lineHeight:Int = txt._fontHeight + lineGap

			While (lineIndex < countLines)
				line = txt._lines[lineIndex]
				i = line.startPos
				l = line.endPos
				xOffset = 0

				While (i < l)
					asc = txt._value[i]
					ac = chars[asc]

					If ac <> Null
						If useKerning
							firstKp = kernPairs.Get(prevChar)
							If firstKp <> Null
								secondKp = firstKp.Get(asc)
								If secondKp <> Null
									xOffset += secondKp.amount
								End
							Endif
						Endif

						ac.Draw(image[ac.page], x + xOffset + line.xOffset, y + yOffset)
						xOffset += ac.xAdvance
						prevChar = asc
					Endif

					i += 1
				Wend

				lineIndex += 1
				yOffset += lineHeight
			Wend
		End Method

	End Class

#ElseIf FLX_TEXT_DRIVER = "fontmachine"

	Class FlxBitmapFont Implements Font

		Method New(fontDescriptionFilePath:String)
			Local text:String = LoadString(fontDescriptionFilePath + ".txt")
			if text = "" Then Print "FONT " + fontDescriptionFilePath + " WAS NOT FOUND!!!"
			LoadFontData(text, fontDescriptionFilePath, False)
		End

		Method GetTextWidth:Float(text:FlxText, fromChar:Int = 0, toChar:Int = -1)
			Local twidth:Float
			Local char:Int
			Local lastchar:Int = 0

			If (toChar < 0) toChar = text._value.Length()

			For Local i:Int = fromChar Until toChar
				char = text._value[i]

				If faceChars[char] <> Null Then
					lastchar = char
					twidth = twidth + faceChars[char].drawingMetrics.drawingWidth + Kerning.x
				End If
			Next

			Return twidth - faceChars[lastchar].drawingMetrics.drawingWidth + faceChars[lastchar].drawingMetrics.drawingSize.x
		End Method

		Method GetTextHeight:Float(text:FlxText)
			Return ( (text._fontHeight + Kerning.y) * text._countLines) - Kerning.y
		End

		Method GetFontHeight:Int(txt:FlxText)
			If faceChars[32] = Null Then Return 0
			Return faceChars[32].drawingMetrics.drawingSize.y
		End Method

		Method DrawText:Void(text:FlxText, x:Float, y:Float)
			If (_drawShadow) DrawChars(text, x, y, shadowChars)
			If (_drawBorder) DrawChars(text, x, y, borderChars)
			DrawChars(text, x, y, faceChars)
		End

		Method Kerning:drawingpoint.DrawingPoint() property
			if _kerning = null Then _kerning = New drawingpoint.DrawingPoint
			Return _kerning
		End

		Method Kerning:void(value:drawingpoint.DrawingPoint) property
			_kerning = value
		End

	Private

		Field _drawShadow:Bool = False
		Field _drawBorder:Bool = False
		Field borderChars:BitMapChar[]
		Field faceChars:BitMapChar[]
		Field shadowChars:BitMapChar[]

		Method LoadFontData:Void(Info:String, fontName:String, dynamicLoad:bool)
			if Info.StartsWith("P1") Then
				LoadPacked(Info,fontName,dynamicLoad)
				return
			EndIf
			Local tokenStream:String[] = Info.Split(",")
			local index:Int = 0
			borderChars = New BitMapChar[65536]
			faceChars = New BitMapChar[65536]
			shadowChars = New BitMapChar[65536]

			Local prefixName:String = fontName
			if prefixName.ToLower().EndsWith(".txt") Then prefixName = prefixName[..-4]

			Local char:Int = 0
			while index<tokenStream.Length
				'We get char to load:
				Local strChar:String = tokenStream[index]
				if strChar.Trim() = "" Then
					'Print "This is going to fail..."
					index+=1
					Exit
				endif
				char = int(strChar)
				'Print "Loading char: " + char + " at index: " + index
				index+=1

				Local kind:String = tokenStream[index]
				'Print "Found kind= " + kind
				index +=1

				Select kind
					Case "{BR"
						index+=3 '3 control point for future use
						borderChars[char] = New BitMapChar
						borderChars[char].drawingMetrics.drawingOffset.x = Int(tokenStream[index])
						borderChars[char].drawingMetrics.drawingOffset.y = Int(tokenStream[index+1])
						borderChars[char].drawingMetrics.drawingSize.x = Int(tokenStream[index+2])
						borderChars[char].drawingMetrics.drawingSize.y = Int(tokenStream[index+3])
						borderChars[char].drawingMetrics.drawingWidth = Int(tokenStream[index+4])
						if dynamicLoad  = False then
							borderChars[char].image = LoadImage(prefixName + "_BORDER_" + char + ".png")
							borderChars[char].image.SetHandle(-borderChars[char].drawingMetrics.drawingOffset.x,-borderChars[char].drawingMetrics.drawingOffset.y)
						Else
							borderChars[char].SetImageResourceName  prefixName + "_BORDER_" + char + ".png"
						endif
						index+=5
						index += 1 ' control point for future use

						_drawBorder = True

					Case "{SH"
						index+=3 '3 control point for future use
						shadowChars[char] = New BitMapChar
						shadowChars[char].drawingMetrics.drawingOffset.x = Int(tokenStream[index])
						shadowChars[char].drawingMetrics.drawingOffset.y = Int(tokenStream[index+1])
						shadowChars[char].drawingMetrics.drawingSize.x = Int(tokenStream[index+2])
						shadowChars[char].drawingMetrics.drawingSize.y = Int(tokenStream[index+3])
						shadowChars[char].drawingMetrics.drawingWidth = Int(tokenStream[index+4])
						Local filename:String = prefixName + "_SHADOW_" + char + ".png"
						if dynamicLoad  = False then
							shadowChars[char].image = LoadImage(filename)
							shadowChars[char].image.SetHandle(-shadowChars[char].drawingMetrics.drawingOffset.x,-shadowChars[char].drawingMetrics.drawingOffset.y)
						Else
							shadowChars[char].SetImageResourceName  filename
						endif

						'shadowChars[char].image = LoadImage(filename)
						'shadowChars[char].image.SetHandle(-shadowChars[char].drawingMetrics.drawingOffset.x,-shadowChars[char].drawingMetrics.drawingOffset.y)

						index+=5
						index += 1 ' control point for future use

						_drawShadow = True

					Case "{FC"
						index+=3 '3 control point for future use
						faceChars[char] = New BitMapChar
						faceChars[char].drawingMetrics.drawingOffset.x = Int(tokenStream[index])
						faceChars[char].drawingMetrics.drawingOffset.y = Int(tokenStream[index+1])
						faceChars[char].drawingMetrics.drawingSize.x = Int(tokenStream[index+2])
						faceChars[char].drawingMetrics.drawingSize.y = Int(tokenStream[index+3])
						faceChars[char].drawingMetrics.drawingWidth = Int(tokenStream[index+4])
						if dynamicLoad = False then
							faceChars[char].image = LoadImage(prefixName + "_" + char + ".png")
							faceChars[char].image.SetHandle(-faceChars[char].drawingMetrics.drawingOffset.x,-faceChars[char].drawingMetrics.drawingOffset.y)
						Else
							faceChars[char].SetImageResourceName prefixName + "_" + char + ".png"
						endif
						index+=5
						index+=1 ' control point for future use

					Default
					Print "Error loading font! Char = " + char

				End
			Wend
			borderChars = borderChars[..char+1]
			faceChars = faceChars[..char+1]
			shadowChars = shadowChars[..char+1]
		End

		Field packedImages:Image[]

		Method LoadPacked:Void(info:String, fontName:String, dynamicLoad:bool)

			Local header:String = info[.. info.Find(",")]

			Local separator:String
			Select header
				Case "P1"
					separator = "."
				Case "P1.01"
					separator = "_P_"
			End Select
			info = info[info.Find(",")+1..]
			borderChars = New BitMapChar[65536]
			faceChars = New BitMapChar[65536]
			shadowChars = New BitMapChar[65536]
			packedImages = New Image[256]
			Local maxPacked:Int = 0
			Local maxChar:Int = 0

			Local prefixName:String = fontName
			if prefixName.ToLower().EndsWith(".txt") Then prefixName = prefixName[..-4]

			Local charList:string[] = info.Split(";")
			For local chr:String = EachIn charList

				Local chrdata:string[] = chr.Split(",")
				if chrdata.Length() <2 Then Exit
				Local char:bitmapchar.BitMapChar
				Local charIndex:Int = int(chrdata[0])
				if maxChar<charIndex Then maxChar = charIndex

				select chrdata[1]
					Case "B"
						borderChars[charIndex] = New BitMapChar
						char = borderChars[charIndex]
						_drawBorder = True
					Case "F"
						faceChars [charIndex] = New BitMapChar
						char = faceChars[charIndex]
					Case "S"
						shadowChars [charIndex] = New BitMapChar
						char = shadowChars[charIndex]
						_drawShadow = True
				End Select
				char.packedFontIndex = Int(chrdata[2])
				if packedImages[char.packedFontIndex] = null Then
					packedImages[char.packedFontIndex] = LoadImage(prefixName + separator + char.packedFontIndex +  ".png")
					if maxPacked<char.packedFontIndex Then maxPacked = char.packedFontIndex
				endif
				char.packedPosition.x = Int(chrdata[3])
				char.packedPosition.y = Int(chrdata[4])
				char.packedSize.x = Int(chrdata[5])
				char.packedSize.y = Int(chrdata[6])
				char.drawingMetrics.drawingOffset.x = Int(chrdata[8])
				char.drawingMetrics.drawingOffset.y = Int(chrdata[9])
				char.drawingMetrics.drawingSize.x = Int(chrdata[10])
				char.drawingMetrics.drawingSize.y = Int(chrdata[11])
				char.drawingMetrics.drawingWidth = Int(chrdata[12])

			Next

			borderChars = borderChars[..maxChar+1]
			faceChars = faceChars[..maxChar+1]
			shadowChars = shadowChars[..maxChar+1]
			packedImages = packedImages[..maxPacked+1]

		End

		Method DrawChars:Void(text:FlxText, x:Float, y:Float, target:BitMapChar[])
			Local lineIndex:Int = 0, countLines:Int = text._countLines, line:FlxTextLine

			Local drx:Int = x, dry:Int = y
			Local i:Int = 0, l:Int
			Local char:Int

			Local lineHeight:Int = text._fontHeight + Kerning.y
			Local targetLength:Int = target.Length()

			While (lineIndex < countLines)
				line = text._lines[lineIndex]
				i = line.startPos
				l = line.endPos
				drx = x + line.xOffset

				While (i < l)
					char = text._value[i]

					If char >= 0 And char <= targetLength And target[char] <> Null Then
						If target[char].packedFontIndex > 0 Then
								DrawImageRect(packedImages[target[char].packedFontIndex], drx + target[char].drawingMetrics.drawingOffset.x, dry + target[char].drawingMetrics.drawingOffset.y, target[char].packedPosition.x, target[char].packedPosition.y, target[char].packedSize.x, target[char].packedSize.y)
						ElseIf target[char].image <> Null Then
							DrawImage(target[char].image, drx, dry)
						EndIf

						drx += faceChars[char].drawingMetrics.drawingWidth + Kerning.x
					End If

					i += 1
				Wend

				lineIndex += 1
				dry += lineHeight
			Wend
		End Method

		Field _kerning:drawingpoint.DrawingPoint
	End
#End

Strict

Import flxextern
Import flxsprite
Import flxcamera
Import flxg
Import system.flxcolor
Import system.flxassetsmanager

Private

#If FLX_TEXT_DRIVER = "angelfont"
	Import vendor.angelfont
#ElseIf FLX_TEXT_DRIVER = "fontmachine"
	Import vendor.fontmachine
#End

Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_8_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_8_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_9_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_9_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_10_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_10_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_11_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_11_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_12_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_12_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_13_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_13_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_14_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_14_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_15_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_15_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_16_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_16_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_17_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_17_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_18_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_18_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_19_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_19_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_20_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_20_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_21_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_21_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_22_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_22_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_23_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_23_flx.${FLX_FONT_EXTENSION}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_24_flx_${FLX_FONT_IMAGE_MASK}"
Import "data/fonts/${FLX_TEXT_DRIVER}/system_font_24_flx.${FLX_FONT_EXTENSION}"

Public

Class FlxText Extends FlxSprite Implements FlxSpriteRenderer

	Global ClassObject:Object

	Const ALIGN_LEFT:Float = 0
	Const ALIGN_CENTER:Float = 0.5
	Const ALIGN_RIGHT:Float = 1

	Const SYSTEM_FONT:String = "system"

Private
	Global _FontLoader:FlxFontLoader = New FlxFontLoader()

	Global _FontsManager:FlxResourcesManager<FlxBitmapFont> = New FlxResourcesManager<FlxBitmapFont>()

	Field _shadow:FlxColor

	Field _value:String

	Field _lines:FlxTextLine[]

	Field _countLines:Int

	Field _alignment:Float

	Field _fontFamily:String

	Field _fontSize:Int

	Field _fontHeight:Int

	Field _fontObject:FlxBitmapFont

Public
	Method New(x:Float, y:Float, width:Int = 0, text:String = "")
		Super.New(x, y)

		SetRenderer(Self)
		_shadow = New FlxColor(0)

		Self.width = width
		frameWidth = Self.width
		moves = False

		SetFormat(SYSTEM_FONT)
		Text = text
	End Method

	Method Destroy:Void()
		Local i:Int = 0, l:Int = _lines.Length()

		While (i < l)
			_lines[i] = Null
			i += 1
		Wend

		_fontObject = Null
		_shadow = Null

		Super.Destroy()
	End Method

	Method SetFormat:FlxText(font:String = "", size:Int = 0, color:Int = FlxG.WHITE, alignment:Float = ALIGN_LEFT, shadowColor:Int = 0)
		_alignment = alignment
		_fontFamily = font
		_fontSize = FlxAssetsManager.GetFont(font).GetValidSize(size)

		Self.Color = color
		Shadow = shadowColor

		_ResetFont()
		_ResetHelpers()

		Return Self
	End Method

	Method SetWidth:Void(width:Float)
		Self.width = width
		frameWidth = Self.width
		_ResetHelpers()
	End Method

	Method Text:String() Property
		Return _value
	End Method

	Method Text:Void(text:String) Property
		_value = text
		_ResetHelpers()
	End Method

	Method Size:Void(size:Int) Property
		_fontSize = FlxAssetsManager.GetFont(_fontFamily).GetValidSize(size)
		_ResetFont()
		_ResetHelpers()
	End Method

	Method Size:Int() Property
		Return _fontSize
	End Method

	Method Font:String() Property
		Return _fontFamily
	End Method

	Method Font:Void(font:String) Property
		_fontFamily = font
		_ResetFont()
		_ResetHelpers()
	End Method

	Method Alignment:Float() Property
		Return _alignment
	End Method

	Method Alignment:Void(alignment:Float) Property
		If (_alignment = alignment) Return
		_alignment = alignment
		_ResetAlignment()
	End Method

	Method Shadow:Int() Property
		Return _shadow.argb
	End Method

	Method Shadow:Void(color:Int) Property
		_shadow.SetARGB(color)
	End Method

	Method SetFontShadowEnabled:Void(enabled:Bool)
	#If FLX_TEXT_DRIVER = "angelfont"
		Return
	#ElseIf FLX_TEXT_DRIVER = "fontmachine"
		_fontObject._drawShadow = enabled
	#End
	End Method

	Method SetFontBoderEnabled:Void(enabled:Bool)
	#If FLX_TEXT_DRIVER = "angelfont"
		Return
	#ElseIf FLX_TEXT_DRIVER = "fontmachine"
		_fontObject._drawBorder = enabled
	#End
	End Method

	Method OnSpriteRender:Void(x:Float, y:Float)
		If (_shadow.argb <> 0) Then
			Local oldColor:Int = FlxG._LastDrawingColor
			Local oldAlpha:Int = FlxG._LastDrawingAlpha

			If (_camera.Color <> FlxG.WHITE) Then
				_mixedColor.MixRGB(_shadow, _camera._color)

				If (FlxG._LastDrawingColor <> _mixedColor.argb) Then
					SetColor(_mixedColor.r, _mixedColor.g, _mixedColor.b)
				End If
			Else
				If (FlxG._LastDrawingColor <> _shadow.argb) Then
					SetColor(_shadow.r, _shadow.g, _shadow.b)
				End If
			End If

			If (_camera.Alpha < 1) Then
				Local _mixedAlpha:Float = _camera.Alpha * _shadow.a

				If (FlxG._LastDrawingAlpha <> _mixedAlpha) Then
					SetAlpha(_mixedAlpha)
				End If
			Else
				If (FlxG._LastDrawingAlpha <> _shadow.a) Then
					SetAlpha(_shadow.a)
				End If
			End If

			_fontObject.DrawText(Self, x + 1, y + 1)

			_mixedColor.SetRGB(oldColor)
			SetColor(_mixedColor.r, _mixedColor.g, _mixedColor.b)
			SetAlpha(oldAlpha)
		End If

		_fontObject.DrawText(Self, x, y)
	End Method

Private
	Method _ResetHelpers:Void()
		If (_value.Length() > 0) Then
			If (width <> 0) Then
				_ParseText()
			Else
				_countLines = 0
				_AddLine(0, _value.Length(), _fontObject.GetTextWidth(Self, 0, _value.Length()))
				_ResetAlignment()
			End If

			Self.height = _GetHeight()
			frameHeight = Self.height
		Else
			Self.height = 0
			frameHeight = 0
		End If

		Super._ResetHelpers()
	End Method

	Method _ResetFont:Void()
		_FontLoader.fontFamily = _fontFamily
		_FontLoader.fontSize = _fontSize

		_fontObject = _FontsManager.GetResource(_fontFamily + _fontSize, _FontLoader)
		_fontHeight = _fontObject.GetFontHeight(Self)
	End Method

	Method _ResetAlignment:Void()
		If (_value.Length() = 0) Return

		For Local line:Int = 0 Until _countLines
			_lines[line].xOffset = (width - _lines[line].width) * _alignment
		Next
	End Method

	Method _GetHeight:Int()
		If (width = 0) Then
			Return _fontHeight
		Else
			Return _fontObject.GetTextHeight(Self)
		End If
	End Method

	Method _ParseText:Void()
		_countLines = 0

		Local prevOffset:Int = 0
		Local offset:Int = -1

		Local i:Int = 0, l:Int = _value.Length()

		While (i < l)
			If (_value[i] = 10 Or _value[i] = KEY_ENTER) Then
				offset = i
				Exit
			End If

			i += 1
		Wend

		If (offset >= 0) Then
			While (offset >= 0)
				_BuildLines(prevOffset, offset)

				prevOffset = offset + 1

				i = prevOffset
				offset = -1

				While (i < l)
					If (_value[i] = 10 Or _value[i] = KEY_ENTER) Then
						offset = i
						Exit
					End If

					i += 1
				Wend
			Wend

			_BuildLines(prevOffset, _value.Length())
		Else
			_BuildLines(0, _value.Length())
		End If

		_ResetAlignment()
	End Method

	Method _BuildLines:Void(startPos:Int, endPos:Int)
		Local textWidth:Int = _fontObject.GetTextWidth(Self, startPos, endPos)

		If (width < textWidth) Then
			Local textLength:Int = endPos - startPos

			Local range:Int = Ceil(textLength / Float(Floor(textWidth / Float(width)) + 1))

			Repeat
				range += 1
				textWidth = _fontObject.GetTextWidth(Self, 0, range)
			Until (textWidth >= width)

			Local maxOffset:Int = range
			Local minOffset:Int = 0
			Local offset:Int = maxOffset
			Local tmpOffset:Int = 0
			Local dirty:Bool = False
			Local finalTextWidth:Int = 0
			Local success:Bool

			Repeat
				Repeat
					offset -= 1
					If (offset - minOffset <= 1) Then
						offset = minOffset + 1
						Exit
					End if

					tmpOffset = -1
					For Local i:Int = offset - 1 To minOffset Step - 1
						success = False

						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							tmpOffset = i
							success = True
							Exit
						End If
					Next

					If (tmpOffset < 0) Then
						tmpOffset = _GetMinOffset(minOffset, offset)
					Else
						If (_value[minOffset] = KEY_SPACE Or _value[minOffset] = KEY_TAB) Then
							For Local i:Int = minOffset To offset
								If (_value[i] <> KEY_SPACE And _value[i] <> KEY_TAB) Then
									minOffset = i
									Exit
								End If
							Next
						End If
					End If

					offset = tmpOffset
					textWidth = _fontObject.GetTextWidth(Self, minOffset, offset)
				Until (textWidth <= width)

				dirty = False
				finalTextWidth = _fontObject.GetTextWidth(Self, minOffset)

				If (finalTextWidth > width And textLength - minOffset > 1) Then
					For Local i:Int = minOffset Until offset
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							minOffset += 1
							dirty = True
						Else
							Exit
						End If
					Next

					For Local i:Int = offset - 1 To minOffset Step - 1
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							offset -= 1
							dirty = True
						Else
							Exit
						End If
					Next

					If (offset - minOffset = 0) Continue

					If ( Not dirty) Then
						_AddLine(minOffset, offset, textWidth)
					Else
						_AddLine(minOffset, offset, _fontObject.GetTextWidth(Self, minOffset, offset))
					End If

					minOffset = offset
					If (success) minOffset += 1

					maxOffset = minOffset + range
					offset = maxOffset
				Else
					Local l:Int = _value.Length()

					For Local i:Int = minOffset Until l
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							minOffset += 1
							dirty = True
						Else
							Exit
						End If
					Next

					For Local i:Int = l - 1 To minOffset Step - 1
						If (_value[i] = KEY_SPACE Or _value[i] = KEY_TAB) Then
							l -= 1
							dirty = True
						Else
							Exit
						End If
					Next

					If (l - minOffset <= 0) Exit

					If ( Not dirty) Then
						_AddLine(minOffset, l, finalTextWidth)
					Else
						_AddLine(minOffset, offset, _fontObject.GetTextWidth(Self, minOffset, l))
					End If

					Exit
				End If
			Forever
		Else
			_AddLine(startPos, endPos, textWidth)
		End If
	End Method

	Method _GetMinOffset:Int(startPos:Int, endPos:Int)
		Local offset:Int = endPos - startPos

		While (_fontObject.GetTextWidth(Self, startPos, startPos + offset) > width)
			offset -= 1
			If (offset = 1) Return offset
		Wend

		Return startPos + offset
	End Method

	Method _AddLine:FlxTextLine(startPos:Int, endPos:Int, width:Int)
		Local line:FlxTextLine

		If (_countLines = _lines.Length()) Then
			_lines = _lines.Resize(_countLines * 2 + 10)
			line = New FlxTextLine(startPos, endPos, width)
			_lines[_countLines] = line
		Else
			line = _lines[_countLines]
			If (line = Null) Then
				line = New FlxTextLine(startPos, endPos, width)
				_lines[_countLines] = line
			Else
				line.Reset(startPos, endPos, width)
			End If
		End If

		_countLines += 1
		Return line
	End Method

End Class

Private
Class FlxTextLine

	Field xOffset:Float

	Field startPos:Int

	Field endPos:Int

	Field width:Int

	Method New(startPos:Int, endPos:Int, width:Int)
		Reset(startPos, endPos, width)
	End Method

	Method Reset:Void(startPos:Int, endPos:Int, width:Int)
		Self.startPos = startPos
		Self.endPos = endPos
		Self.width = width
	End Method

End Class

Class FlxFontLoader Extends FlxResourceLoader<FlxBitmapFont>

	Field fontFamily:String = FlxText.SYSTEM_FONT
	Field fontSize:Int

	Method Load:FlxBitmapFont(name:String)
		Return New FlxBitmapFont(FlxAssetsManager.GetFont(fontFamily).GetPath(fontSize))
	End Method

End Class

#If FLX_TEXT_DRIVER = "angelfont"
	Class FlxBitmapFont
	Private
		Field image:Image[] = New Image[1]
		Field chars:Char[256]

		Field kernPairs:IntMap<IntMap<KernPair>> = New IntMap<IntMap<KernPair>>
		Global firstKp:IntMap<KernPair>
		Global secondKp:KernPair

		Field iniText:String

		Field xOffset:Int
		Field yOffset:Int

	Public
		Field useKerning:Bool = True

		Field height:Int = 0
		Field heightOffset:Int = 9999

		Field lineGap:Int = 5

		Method New(url:String)
			iniText = LoadString(url+".fnt")
			Local lines:String[] = iniText.Split(String.FromChar(10))
			Local firstLine:String = lines[0]
			If firstLine.Contains("<?xml")
				Local lineList:List<String> = New List<String>(lines)
				lineList.RemoveFirst()
				lines = lineList.ToArray()
				iniText = "~n".Join(lines)
			End

			Local pageCount:Int = 0

			Local config:= LoadConfig(iniText)

			Local nodes:= config.FindNodesByPath("font/chars/char")

			For Local node:= EachIn nodes
				Local id:Int = Int(node.GetAttribute("id"))
				Local page:Int = Int(node.GetAttribute("page"))
				If pageCount < page pageCount = page
				chars[id] = New Char(Int(node.GetAttribute("x")), Int(node.GetAttribute("y")), Int(node.GetAttribute("width")), Int(node.GetAttribute("height")),  Int(node.GetAttribute("xoffset")),  Int(node.GetAttribute("yoffset")),  Int(node.GetAttribute("xadvance")), page)
				Local ch := chars[id]
				If ch.height > Self.height Self.height = ch.height
				If ch.yOffset < Self.heightOffset Self.heightOffset = ch.yOffset
			Next

			nodes = config.FindNodesByPath("font/kernings/kerning")

			If (nodes <> Null) Then
				For Local node:= EachIn nodes
					Local first:Int = Int(node.GetAttribute("first"))
					firstKp = kernPairs.Get(first)
					If firstKp = Null
						kernPairs.Add(first, New IntMap<KernPair>)
						firstKp = kernPairs.Get(first)
					End

					Local second:Int = Int(node.GetAttribute("second"))
					firstKp.Add(second, New KernPair(first, second, Int(node.GetAttribute("amount"))))
				End
			Else
				useKerning = False
			End If

			If pageCount = 0
				image[0] = LoadImage(url+".png")
				If image[0] = Null image[0] = LoadImage(url + "_0.png")
			Else
				For Local page:= 0 To pageCount
					If image.Length < page+1 image = image.Resize(page+1)
					image[page] = LoadImage(url+"_"+page+".png")
				End
			End
		End

		Method GetTextWidth:Int(txt:FlxText, startPos:Int = 0, endPos:Int = -1)
			Local prevChar:Int = 0
			Local width:Int = 0

			If (endPos < 0) endPos = txt._value.Length()
			Local asc:Int, ac:Char

			For Local i:= startPos Until endPos
				asc = txt._value[i]
				ac = chars[asc]

				If ac <> Null
					If useKerning
						Local firstKp:= kernPairs.Get(prevChar)
						If firstKp <> Null
							Local secondKp:= firstKp.Get(asc)
							If secondKp <> Null
								xOffset += secondKp.amount
							End
						Endif
					EndIf

					width += ac.xAdvance
					prevChar = asc
				EndIf
			Next

			Return width
		End Method

		Method GetFontHeight:Int(txt:FlxText)
			Local h:Int = 0

			If (txt._value.Length() <> 0) Then
				Local l:Int = txt._value.Length()

				For Local i:= 0 Until l
					Local asc:Int = txt._value[i]
					Local ac:Char = chars[asc]
					If ac.height+ac.yOffset > h h = ac.height+ac.yOffset
				Next
			Else
				h = chars[KEY_SPACE].height + chars[KEY_SPACE].yOffset
			End If

			Return h
		End

		Method GetTextHeight:Int(txt:FlxText)
			Return ( (txt._fontHeight + lineGap) * txt._countLines) - lineGap
		End Method

		Method DrawText:Void(txt:FlxText, x:Int, y:Int)
			Local lineIndex:Int = 0, countLines:Int = txt._countLines, line:FlxTextLine

			Local prevChar:Int = 0, xOffset:Int = 0, yOffset:Int = 0
			Local i:Int = 0, l:Int
			Local asc:Int, ac:Char
			Local lineHeight:Int = txt._fontHeight + lineGap

			While (lineIndex < countLines)
				line = txt._lines[lineIndex]
				i = line.startPos
				l = line.endPos
				xOffset = 0

				While (i < l)
					asc = txt._value[i]
					ac = chars[asc]

					If ac <> Null
						If useKerning
							firstKp = kernPairs.Get(prevChar)
							If firstKp <> Null
								secondKp = firstKp.Get(asc)
								If secondKp <> Null
									xOffset += secondKp.amount
								End
							Endif
						Endif

						ac.Draw(image[ac.page], x + xOffset + line.xOffset, y + yOffset)
						xOffset += ac.xAdvance
						prevChar = asc
					Endif

					i += 1
				Wend

				lineIndex += 1
				yOffset += lineHeight
			Wend
		End Method

	End Class

#ElseIf FLX_TEXT_DRIVER = "fontmachine"

	Class FlxBitmapFont Implements Font

		Method New(fontDescriptionFilePath:String)
			Local text:String = LoadString(fontDescriptionFilePath + ".txt")
			if text = "" Then Print "FONT " + fontDescriptionFilePath + " WAS NOT FOUND!!!"
			LoadFontData(text, fontDescriptionFilePath, False)
		End

		Method GetTextWidth:Float(text:FlxText, fromChar:Int = 0, toChar:Int = -1)
			Local twidth:Float
			Local char:Int
			Local lastchar:Int = 0

			If (toChar < 0) toChar = text._value.Length()

			For Local i:Int = fromChar Until toChar
				char = text._value[i]

				If faceChars[char] <> Null Then
					lastchar = char
					twidth = twidth + faceChars[char].drawingMetrics.drawingWidth + Kerning.x
				End If
			Next

			Return twidth - faceChars[lastchar].drawingMetrics.drawingWidth + faceChars[lastchar].drawingMetrics.drawingSize.x
		End Method

		Method GetTextHeight:Float(text:FlxText)
			Return ( (text._fontHeight + Kerning.y) * text._countLines) - Kerning.y
		End

		Method GetFontHeight:Int(txt:FlxText)
			If faceChars[32] = Null Then Return 0
			Return faceChars[32].drawingMetrics.drawingSize.y
		End Method

		Method DrawText:Void(text:FlxText, x:Float, y:Float)
			If (_drawShadow) DrawChars(text, x, y, shadowChars)
			If (_drawBorder) DrawChars(text, x, y, borderChars)
			DrawChars(text, x, y, faceChars)
		End

		Method Kerning:drawingpoint.DrawingPoint() property
			if _kerning = null Then _kerning = New drawingpoint.DrawingPoint
			Return _kerning
		End

		Method Kerning:void(value:drawingpoint.DrawingPoint) property
			_kerning = value
		End

	Private

		Field _drawShadow:Bool = False
		Field _drawBorder:Bool = False
		Field borderChars:BitMapChar[]
		Field faceChars:BitMapChar[]
		Field shadowChars:BitMapChar[]

		Method LoadFontData:Void(Info:String, fontName:String, dynamicLoad:bool)
			if Info.StartsWith("P1") Then
				LoadPacked(Info,fontName,dynamicLoad)
				return
			EndIf
			Local tokenStream:String[] = Info.Split(",")
			local index:Int = 0
			borderChars = New BitMapChar[65536]
			faceChars = New BitMapChar[65536]
			shadowChars = New BitMapChar[65536]

			Local prefixName:String = fontName
			if prefixName.ToLower().EndsWith(".txt") Then prefixName = prefixName[..-4]

			Local char:Int = 0
			while index<tokenStream.Length
				'We get char to load:
				Local strChar:String = tokenStream[index]
				if strChar.Trim() = "" Then
					'Print "This is going to fail..."
					index+=1
					Exit
				endif
				char = int(strChar)
				'Print "Loading char: " + char + " at index: " + index
				index+=1

				Local kind:String = tokenStream[index]
				'Print "Found kind= " + kind
				index +=1

				Select kind
					Case "{BR"
						index+=3 '3 control point for future use
						borderChars[char] = New BitMapChar
						borderChars[char].drawingMetrics.drawingOffset.x = Int(tokenStream[index])
						borderChars[char].drawingMetrics.drawingOffset.y = Int(tokenStream[index+1])
						borderChars[char].drawingMetrics.drawingSize.x = Int(tokenStream[index+2])
						borderChars[char].drawingMetrics.drawingSize.y = Int(tokenStream[index+3])
						borderChars[char].drawingMetrics.drawingWidth = Int(tokenStream[index+4])
						if dynamicLoad  = False then
							borderChars[char].image = LoadImage(prefixName + "_BORDER_" + char + ".png")
							borderChars[char].image.SetHandle(-borderChars[char].drawingMetrics.drawingOffset.x,-borderChars[char].drawingMetrics.drawingOffset.y)
						Else
							borderChars[char].SetImageResourceName  prefixName + "_BORDER_" + char + ".png"
						endif
						index+=5
						index += 1 ' control point for future use

						_drawBorder = True

					Case "{SH"
						index+=3 '3 control point for future use
						shadowChars[char] = New BitMapChar
						shadowChars[char].drawingMetrics.drawingOffset.x = Int(tokenStream[index])
						shadowChars[char].drawingMetrics.drawingOffset.y = Int(tokenStream[index+1])
						shadowChars[char].drawingMetrics.drawingSize.x = Int(tokenStream[index+2])
						shadowChars[char].drawingMetrics.drawingSize.y = Int(tokenStream[index+3])
						shadowChars[char].drawingMetrics.drawingWidth = Int(tokenStream[index+4])
						Local filename:String = prefixName + "_SHADOW_" + char + ".png"
						if dynamicLoad  = False then
							shadowChars[char].image = LoadImage(filename)
							shadowChars[char].image.SetHandle(-shadowChars[char].drawingMetrics.drawingOffset.x,-shadowChars[char].drawingMetrics.drawingOffset.y)
						Else
							shadowChars[char].SetImageResourceName  filename
						endif

						'shadowChars[char].image = LoadImage(filename)
						'shadowChars[char].image.SetHandle(-shadowChars[char].drawingMetrics.drawingOffset.x,-shadowChars[char].drawingMetrics.drawingOffset.y)

						index+=5
						index += 1 ' control point for future use

						_drawShadow = True

					Case "{FC"
						index+=3 '3 control point for future use
						faceChars[char] = New BitMapChar
						faceChars[char].drawingMetrics.drawingOffset.x = Int(tokenStream[index])
						faceChars[char].drawingMetrics.drawingOffset.y = Int(tokenStream[index+1])
						faceChars[char].drawingMetrics.drawingSize.x = Int(tokenStream[index+2])
						faceChars[char].drawingMetrics.drawingSize.y = Int(tokenStream[index+3])
						faceChars[char].drawingMetrics.drawingWidth = Int(tokenStream[index+4])
						if dynamicLoad = False then
							faceChars[char].image = LoadImage(prefixName + "_" + char + ".png")
							faceChars[char].image.SetHandle(-faceChars[char].drawingMetrics.drawingOffset.x,-faceChars[char].drawingMetrics.drawingOffset.y)
						Else
							faceChars[char].SetImageResourceName prefixName + "_" + char + ".png"
						endif
						index+=5
						index+=1 ' control point for future use

					Default
					Print "Error loading font! Char = " + char

				End
			Wend
			borderChars = borderChars[..char+1]
			faceChars = faceChars[..char+1]
			shadowChars = shadowChars[..char+1]
		End

		Field packedImages:Image[]

		Method LoadPacked:Void(info:String, fontName:String, dynamicLoad:bool)

			Local header:String = info[.. info.Find(",")]

			Local separator:String
			Select header
				Case "P1"
					separator = "."
				Case "P1.01"
					separator = "_P_"
			End Select
			info = info[info.Find(",")+1..]
			borderChars = New BitMapChar[65536]
			faceChars = New BitMapChar[65536]
			shadowChars = New BitMapChar[65536]
			packedImages = New Image[256]
			Local maxPacked:Int = 0
			Local maxChar:Int = 0

			Local prefixName:String = fontName
			if prefixName.ToLower().EndsWith(".txt") Then prefixName = prefixName[..-4]

			Local charList:string[] = info.Split(";")
			For local chr:String = EachIn charList

				Local chrdata:string[] = chr.Split(",")
				if chrdata.Length() <2 Then Exit
				Local char:bitmapchar.BitMapChar
				Local charIndex:Int = int(chrdata[0])
				if maxChar<charIndex Then maxChar = charIndex

				select chrdata[1]
					Case "B"
						borderChars[charIndex] = New BitMapChar
						char = borderChars[charIndex]
						_drawBorder = True
					Case "F"
						faceChars [charIndex] = New BitMapChar
						char = faceChars[charIndex]
					Case "S"
						shadowChars [charIndex] = New BitMapChar
						char = shadowChars[charIndex]
						_drawShadow = True
				End Select
				char.packedFontIndex = Int(chrdata[2])
				if packedImages[char.packedFontIndex] = null Then
					packedImages[char.packedFontIndex] = LoadImage(prefixName + separator + char.packedFontIndex +  ".png")
					if maxPacked<char.packedFontIndex Then maxPacked = char.packedFontIndex
				endif
				char.packedPosition.x = Int(chrdata[3])
				char.packedPosition.y = Int(chrdata[4])
				char.packedSize.x = Int(chrdata[5])
				char.packedSize.y = Int(chrdata[6])
				char.drawingMetrics.drawingOffset.x = Int(chrdata[8])
				char.drawingMetrics.drawingOffset.y = Int(chrdata[9])
				char.drawingMetrics.drawingSize.x = Int(chrdata[10])
				char.drawingMetrics.drawingSize.y = Int(chrdata[11])
				char.drawingMetrics.drawingWidth = Int(chrdata[12])

			Next

			borderChars = borderChars[..maxChar+1]
			faceChars = faceChars[..maxChar+1]
			shadowChars = shadowChars[..maxChar+1]
			packedImages = packedImages[..maxPacked+1]

		End

		Method DrawChars:Void(text:FlxText, x:Float, y:Float, target:BitMapChar[])
			Local lineIndex:Int = 0, countLines:Int = text._countLines, line:FlxTextLine

			Local drx:Int = x, dry:Int = y
			Local i:Int = 0, l:Int
			Local char:Int

			Local lineHeight:Int = text._fontHeight + Kerning.y
			Local targetLength:Int = target.Length()

			While (lineIndex < countLines)
				line = text._lines[lineIndex]
				i = line.startPos
				l = line.endPos
				drx = x + line.xOffset

				While (i < l)
					char = text._value[i]

					If char >= 0 And char <= targetLength And target[char] <> Null Then
						If target[char].packedFontIndex > 0 Then
								DrawImageRect(packedImages[target[char].packedFontIndex], drx + target[char].drawingMetrics.drawingOffset.x, dry + target[char].drawingMetrics.drawingOffset.y, target[char].packedPosition.x, target[char].packedPosition.y, target[char].packedSize.x, target[char].packedSize.y)
						ElseIf target[char].image <> Null Then
							DrawImage(target[char].image, drx, dry)
						EndIf

						drx += faceChars[char].drawingMetrics.drawingWidth + Kerning.x
					End If

					i += 1
				Wend

				lineIndex += 1
				dry += lineHeight
			Wend
		End Method

		Field _kerning:drawingpoint.DrawingPoint
	End
#End


throw InvalidArgumentException on division by zero in Vector2D

Strict

Private

Import bono

Public

Class Vector2D
    Field x:Float
    Field y:Float

    Method New(x:Float=0, y:Float=0)
        Self.x = x
        Self.y = y
    End

    Method IsZero:Bool()
        Return (x = 0 And y = 0)
    End

    Method Length:Float()
        Return Sqrt(x * x + y * y)
    End

    Method DotProduct:Float(v2:Vector2D)
        Return (x * v2.x) + (y * v2.y)
    End

    Method CrossProduct:Float(v2:Vector2D)
        Return (x * v2.y) - (y * v2.x)
    End

    Method Distance:Float(v2:Vector2D)
        Return Sqrt(Pow(v2.x - x, 2.0) + Pow(v2.y - y, 2.0))
    End

    Method Equal:Bool(v2:Vector2D)
        Return (x = v2.x And y = v2.y)
    End

    Method Copy:Vector2D()
        Return New Vector2D(x, y)
    End

    Method Reset:Vector2D()
        x = 0
        y = 0

        Return Self
    End

    Method Round:Vector2D()
        x = MathHelper.Round(x)
        y = MathHelper.Round(y)

        Return Self
    End

    Method Ceil:Vector2D()
        x = monkey.Ceil(x)
        y = monkey.Ceil(y)

        Return Self
    End

    Method Floor:Vector2D()
        x = monkey.Floor(x)
        y = monkey.Floor(y)

        Return Self
    End

    Method Set:Vector2D(v2:Vector2D)
        x = v2.x
        y = v2.y

        Return Self
    End

    Method Normalize:Vector2D()
        Local length:Float = Length()
        If length = 0 Then Return Self

        x /= length
        y /= length

        Return Self
    End

    Method Rotate:Vector2D(angle:Float)
        Local tmpX:Float = (x * Cos(angle)) - (y * Sin(angle))
        Local tmpY:Float = (y * Cos(angle)) - (x * Sin(angle))

        x = tmpX
        y = tmpY

        Return Self
    End

    Method Revert:Vector2D()
        x *= -1
        y *= -1

        Return Self
    End

    Method Add:Vector2D(v2:Vector2D)
        x += v2.x
        y += v2.y
        Return Self
    End

    Method Add:Vector2D(factor:Float)
        x += factor
        y += factor
        Return Self
    End

    Method Sub:Vector2D(v2:Vector2D)
        x -= v2.x
        y -= v2.y
        Return Self
    End

    Method Sub:Vector2D(factor:Float)
        x -= factor
        y -= factor
        Return Self
    End

    Method Mul:Vector2D(v2:Vector2D)
        x *= v2.x
        y *= v2.y
        Return Self
    End

    Method Mul:Vector2D(factor:Float)
        x *= factor
        y *= factor
        Return Self
    End

    Method Div:Vector2D(v2:Vector2D)
        If v2.x = 0 Or v2.y = 0
            Throw New InvalidArgumentException("Division by zero: " + v2)
        End

        x /= v2.x
        y /= v2.y
        Return Self
    End

    Method Div:Vector2D(factor:Float)
        If factor = 0
            Throw New InvalidArgumentException("Division by zero")
        End

        y /= factor
        x /= factor
        Return Self
    End

    Method ToString:String()
        Return "(" + x + ", " + y + ")"
    End
End

Strict

Private

Import bono

Public

Class Vector2D
    Field x:Float
    Field y:Float

    Method New(x:Float=0, y:Float=0)
        Self.x = x
        Self.y = y
    End

    Method IsZero:Bool()
        Return (x = 0 And y = 0)
    End

    Method Length:Float()
        Return Sqrt(x * x + y * y)
    End

    Method DotProduct:Float(v2:Vector2D)
        Return (x * v2.x) + (y * v2.y)
    End

    Method CrossProduct:Float(v2:Vector2D)
        Return (x * v2.y) - (y * v2.x)
    End

    Method Distance:Float(v2:Vector2D)
        Return Sqrt(Pow(v2.x - x, 2.0) + Pow(v2.y - y, 2.0))
    End

    Method Equal:Bool(v2:Vector2D)
        Return (x = v2.x And y = v2.y)
    End

    Method Copy:Vector2D()
        Return New Vector2D(x, y)
    End

    Method Reset:Vector2D()
        x = 0
        y = 0

        Return Self
    End

    Method Round:Vector2D()
        x = MathHelper.Round(x)
        y = MathHelper.Round(y)

        Return Self
    End

    Method Ceil:Vector2D()
        x = monkey.Ceil(x)
        y = monkey.Ceil(y)

        Return Self
    End

    Method Floor:Vector2D()
        x = monkey.Floor(x)
        y = monkey.Floor(y)

        Return Self
    End

    Method Set:Vector2D(v2:Vector2D)
        x = v2.x
        y = v2.y

        Return Self
    End

    Method Normalize:Vector2D()
        Local length:Float = Length()
        If length = 0 Then Return Self

        x /= length
        y /= length

        Return Self
    End

    Method Rotate:Vector2D(angle:Float)
        Local tmpX:Float = (x * Cos(angle)) - (y * Sin(angle))
        Local tmpY:Float = (y * Cos(angle)) - (x * Sin(angle))

        x = tmpX
        y = tmpY

        Return Self
    End

    Method Revert:Vector2D()
        x *= -1
        y *= -1

        Return Self
    End

    Method Add:Vector2D(v2:Vector2D)
        x += v2.x
        y += v2.y
        Return Self
    End

    Method Add:Vector2D(factor:Float)
        x += factor
        y += factor
        Return Self
    End

    Method Sub:Vector2D(v2:Vector2D)
        x -= v2.x
        y -= v2.y
        Return Self
    End

    Method Sub:Vector2D(factor:Float)
        x -= factor
        y -= factor
        Return Self
    End

    Method Mul:Vector2D(v2:Vector2D)
        x *= v2.x
        y *= v2.y
        Return Self
    End

    Method Mul:Vector2D(factor:Float)
        x *= factor
        y *= factor
        Return Self
    End

    Method Div:Vector2D(v2:Vector2D)
        x /= v2.x
        y /= v2.y
        Return Self
    End

    Method Div:Vector2D(factor:Float)
        y /= factor
        x /= factor
        Return Self
    End

    Method ToString:String()
        Return "(" + x + ", " + y + ")"
    End
End


Fixed GUI example

Fixed GUI example

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

Strict

Import diddy

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

Global guiScreen:GUIScreen

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		images.Load("button.png", "", False)
		images.Load("buttonClick.png", "", False)
		images.Load("check.png", "", False)
		images.Load("checkClick.png", "", False)
		drawFPSOn = True
		guiScreen = new GUIScreen
		guiScreen.PreStart()
		return 0
	End
End

Class GUIScreen Extends Screen
	Field mygui:MyGUI = New MyGUI

	Method New()
		name = "Game"
	End

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

	End

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

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

End

Class MyGUI Extends GUI
	Field button:Button
	Field toggleButton:Button
	Field slider:Slider
	Field buttonImage:GameImage

	Method New()
		button = New Button(desktop, game.images.Find("button"), game.images.Find("buttonClick"))
		button.SetBounds(150,50,100,50)
		button.Text ("HELLO", 0.5, 0.5)

		toggleButton = New Button(desktop, game.images.Find("check"))
		toggleButton.toggle = True
		toggleButton.SetBounds(50,120,100,50)
		toggleButton.StyleSelected.image = game.images.Find("checkClick")
		toggleButton.StyleSelected.drawBackground = False
	'	toggleButton.StyleNormal = cs
'		toggleButton.StyleNormal.red = 0
'		toggleButton.StyleNormal.green = 0
'		toggleButton.StyleNormal.blue = 255
'		toggleButton.StyleSelected.red = 0
'		toggleButton.StyleSelected.green = 255
'		toggleButton.StyleSelected.blue = 0

		slider = New Slider(desktop)
		slider.SetBounds(50,190,200,20)
		slider.ShowButtons = True
		slider.StyleNormal.red = 0
		slider.StyleNormal.green = 0
		slider.StyleNormal.blue = 255

	End

	Method ActionPerformed:Void(source:Component, action:String)
		If source = slider And action = ACTION_VALUE_CHANGED Then
			Print "slider="+Slider(source).value
		ElseIf source = button And action = ACTION_CLICKED Then
			Print("pressed button!")
		ElseIf source = toggleButton And action = ACTION_CLICKED Then
			If toggleButton.selected Then
				Print("pressed toggleButton! selected=true")
			Else
				Print("pressed toggleButton! selected=false")
			End
		End
	End
End

Strict

Import diddy

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

Global guiScreen:GUIScreen

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		images.Load("button.png", "", False)
		images.Load("buttonClick.png", "", False)
		images.Load("check.png", "", False)
		images.Load("checkClick.png", "", False)
		drawFPSOn = True
		guiScreen = new GUIScreen
		guiScreen.PreStart()
		return 0
	End
End

Class GUIScreen Extends Screen
	Field mygui:MyGUI = New MyGUI

	Method New()
		name = "Game"
	End

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

	End

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

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

End

Class MyGUI Extends GUI
	Field button:Button
	Field toggleButton:Button
	Field slider:Slider
	Field buttonImage:GameImage

	Method New()
		button = New Button(desktop, game.images.Find("button"), game.images.Find("buttonClick"))
		button.SetBounds(150,50,100,50)
		button.text = "HELLO"

		toggleButton = New Button(desktop, game.images.Find("check"))
		toggleButton.toggle = True
		toggleButton.SetBounds(50,120,100,50)
		toggleButton.StyleSelected.image = game.images.Find("checkClick")
		toggleButton.StyleSelected.drawBackground = False
	'	toggleButton.StyleNormal = cs
'		toggleButton.StyleNormal.red = 0
'		toggleButton.StyleNormal.green = 0
'		toggleButton.StyleNormal.blue = 255
'		toggleButton.StyleSelected.red = 0
'		toggleButton.StyleSelected.green = 255
'		toggleButton.StyleSelected.blue = 0

		slider = New Slider(desktop)
		slider.SetBounds(50,190,200,20)
		slider.ShowButtons = True
		slider.StyleNormal.red = 0
		slider.StyleNormal.green = 0
		slider.StyleNormal.blue = 255

	End

	Method ActionPerformed:Void(source:Component, action:String)
		If source = slider And action = ACTION_VALUE_CHANGED Then
			Print "slider="+Slider(source).value
		ElseIf source = button And action = ACTION_CLICKED Then
			Print("pressed button!")
		ElseIf source = toggleButton And action = ACTION_CLICKED Then
			If toggleButton.selected Then
				Print("pressed toggleButton! selected=true")
			Else
				Print("pressed toggleButton! selected=false")
			End
		End
	End
End


add missing import statement

add missing import statement

Strict

Private

Import bono
Import mojo.app

Public

Class DeltaTimer
    Private

    Field _delta:Float
    Field _frameTime:Float
    Field _millisecs:Float
    Field lastMillisecs:Float
    Field targetFps:Float
    Field paused:Bool
    Field pauseOffset:Float

    Public

    Method New()
        Throw New InvalidConstructorException("use New(Float)")
    End

    Method New(fps:Float)
        targetFps = fps
        lastMillisecs = Millisecs()
    End

    Method OnUpdate:Void()
        If paused Then Return

        _millisecs = Millisecs() - pauseOffset
        _frameTime = _millisecs - lastMillisecs
        _delta = frameTime / (1000.0 / targetFps)
        lastMillisecs = _millisecs
    End

    Method Play:Void()
        If Not paused Then Return
        paused = False

        lastMillisecs = Millisecs() - pauseOffset
    End

    Method Pause:Void()
        If paused Then Return
        paused = True

        Local now:Float = Millisecs()
        pauseOffset = now - lastMillisecs
    End

    Method IsPaused:Bool()
        Return paused
    End

    Method millisecs:Float() Property
        Return _millisecs
    End

    Method delta:Float() Property
        If paused Then Return 0
        Return _delta
    End

    Method frameTime:Float() Property
        If paused Then Return 0
        Return _frameTime
    End
End

Strict

Private

Import mojo.app

Public

Class DeltaTimer
    Private

    Field _delta:Float
    Field _frameTime:Float
    Field _millisecs:Float
    Field lastMillisecs:Float
    Field targetFps:Float
    Field paused:Bool
    Field pauseOffset:Float

    Public

    Method New()
        Throw New InvalidConstructorException("use New(Float)")
    End

    Method New(fps:Float)
        targetFps = fps
        lastMillisecs = Millisecs()
    End

    Method OnUpdate:Void()
        If paused Then Return

        _millisecs = Millisecs() - pauseOffset
        _frameTime = _millisecs - lastMillisecs
        _delta = frameTime / (1000.0 / targetFps)
        lastMillisecs = _millisecs
    End

    Method Play:Void()
        If Not paused Then Return
        paused = False

        lastMillisecs = Millisecs() - pauseOffset
    End

    Method Pause:Void()
        If paused Then Return
        paused = True

        Local now:Float = Millisecs()
        pauseOffset = now - lastMillisecs
    End

    Method IsPaused:Bool()
        Return paused
    End

    Method millisecs:Float() Property
        Return _millisecs
    End

    Method delta:Float() Property
        If paused Then Return 0
        Return _delta
    End

    Method frameTime:Float() Property
        If paused Then Return 0
        Return _frameTime
    End
End


FPS fixed

FPS fixed

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 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
		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()
		SetUpdateRate(FlxG.framerate)
		Seed = SystemMillisecs()

		FlxG.deviceWidth = DeviceWidth()
		FlxG.deviceHeight = DeviceHeight()

		If (FlxG.targetWidth = 0) FlxG.targetWidth = FlxG.width
		If (FlxG.targetHeight = 0) FlxG.targetHeight = FlxG.height

		FlxG._deviceScaleFactorX = FlxG.deviceWidth / Float(FlxG.targetWidth)
		FlxG._deviceScaleFactorY = FlxG.deviceHeight / Float(FlxG.targetHeight)

		_InitData()
		_step = 1000.0 / FlxG.framerate
		_fps = -1
		_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)
			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 = []
			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 _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 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 _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
		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()
		SetUpdateRate(FlxG.framerate)
		Seed = SystemMillisecs()

		FlxG.deviceWidth = DeviceWidth()
		FlxG.deviceHeight = DeviceHeight()

		If (FlxG.targetWidth = 0) FlxG.targetWidth = FlxG.width
		If (FlxG.targetHeight = 0) FlxG.targetHeight = FlxG.height

		FlxG._deviceScaleFactorX = FlxG.deviceWidth / Float(FlxG.targetWidth)
		FlxG._deviceScaleFactorY = FlxG.deviceHeight / Float(FlxG.targetHeight)

		_InitData()
		_step = 1000.0 / FlxG.framerate
		_fps = -1
		_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
		Else
			If (Millisecs() - _fpsTime > 1000) Then
				_fps = _fpsCounter * 5
				_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)
			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 = []
			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 _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


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

Strict
Import mojo

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		Self.ptr+=1
		Return result
	End Method

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

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

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

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

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

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

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

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

Strict
Import mojo

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

	Method New()
		Self.LoadAll()
	End

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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