FlxReplay fixed

FlxReplay fixed

Strict

Import mojo

Import flixel.flxg
Import replay.keyrecord
Import replay.xyrecord
Import replay.xyzrecord
Import replay.framerecord

Class FlxReplay

	Field seed:Float

	Field frame:Int

	Field frameCount:Int

	Field elapsed:Float

	Field lastElapsed:Float

	Field finished:Bool

Private
	Field _frames:FrameRecord[]

	Field _capacity:Int

	Field _marker:Int

Public
	Method New()
		seed = 0
		frame = 0
		frameCount = 0
		elapsed = 0
		lastElapsed = 0
		finished = False
		_frames = []
		_capacity = 0
		_marker = 0
	End Method

	Method Destroy:Void()
		If (_frames.Length() = 0) Return

		Local i:Int = frameCount - 1

		While (i >= 0)
			_frames[i].Destroy()
			i -= 1
		Wend

		_frames = []
	End Method

	Method Create:Void(seed:Int)
		Destroy()
		_Init()
		Self.seed = seed
		Rewind()
	End Method

	Method Load:Void(fileContents:String)
		_Init()

		Local lines:String[] = fileContents.Split("~n")

		seed = Int(lines[0])

		Local line:String
		Local i:Int = 1
		Local l:Int = lines.Length()

		While (i < l)
			line = lines[i]

			If (line.Length() > 3) Then
				_frames[frameCount] = (New FrameRecord()).Load(line)
				frameCount += 1

				If (frameCount >= _capacity) Then
					_capacity *= 2
					_frames = _frames.Resize(_capacity)
				End If
			End If

			i += 1
		Wend

		Rewind()
	End Method

	Method Save:String()
		If (frameCount <= 0) Return ""

		Local output:StringStack = New StringStack()
		output.Push(FlxG.GlobalSeed + "~n")

		Local i:Int = 0
		While (i < frameCount)
			_frames[i].Save(output)
			output.Push("~n")
			i += 1
		Wend

		Return output.Join("")
	End Method

	Method RecordFrame:Void()
		Local accelRecord:XYZRecord
		Local joystickRecord:Stack<XYZRecord[]>
		Local touchRecord:Stack<XYRecord>
		Local keysRecord:Stack<KeyRecord>
		Local mouseRecord:XYRecord

		#If TARGET = "html5" Or TARGET = "ios" Or TARGET = "android"
			accelRecord = FlxG.Accel.RecordXYZ()
		#End

		#If TARGET = "xna" Or TARGET = "glfw"
			If (Not FlxG.Mobile) Then
				Local joyCount:Int = FlxG.JoystickCount()
				Local joyXYZRecord:XYZRecord[]

				For Local i:Int = 0 Until joyCount
					joyXYZRecord = FlxG.Joystick(i).RecordXYZ()

					If (joyXYZRecord.Length() > 0) Then
						If (joystickRecord = Null) joystickRecord = New Stack<XYZRecord[]>()
						joystickRecord.Insert(i, joyXYZRecord)
					End If

					keysRecord = FlxG.Joystick(i).RecordKeys(keysRecord)
				Next
			End If
		#End

		#If TARGET = "ios" Or TARGET = "android"
			Local touchCount:Int = FlxG.TouchCount()
			Local touchXYRecord:XYRecord

			For Local i:Int = 0 Until touchCount
				If (i > 0 And Not FlxG.Touch(i).Used) Exit

				touchXYRecord = FlxG.Touch(i).RecordXY()

				If (touchXYRecord <> Null) Then
					If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
					touchRecord.Insert(i, touchXYRecord)
				End If

				keysRecord = FlxG.Touch(i).RecordKeys(keysRecord)
			Next

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

		#ElseIf TARGET = "xna"
			If (Not FlxG.Mobile) Then
				Local touchXYRecord:XYRecord = FlxG.Touch(0).RecordXY()

				If (touchXYRecord <> Null) Then
					If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
					touchRecord.Insert(0, touchXYRecord)
				End If

				keysRecord = FlxG.Touch(0).RecordKeys(keysRecord)
				keysRecord = FlxG.Keys.RecordKeys(keysRecord)
			Else
				Local touchCount:Int = FlxG.TouchCount()
				Local touchXYRecord:XYRecord

				For Local i:Int = 0 Until touchCount
					If (i > 0 And Not FlxG.Touch(i).Used) Exit

					touchXYRecord = FlxG.Touch(i).RecordXY()

					If (touchXYRecord <> Null) Then
						If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
						touchRecord.Insert(i, touchXYRecord)
					End If

					keysRecord = FlxG.Touch(i).RecordKeys(keysRecord)
				Next
			End If

		#Else
			keysRecord = FlxG.Keys.RecordKeys(keysRecord)
		#End

		#If TARGET = "android"
			keysRecord = FlxG.Keys.RecordKeys(keysRecord)
		#End

		#If TARGET = "html5" Or TARGET = "glfw"
			Local touchXYRecord:XYRecord = FlxG.Touch(0).RecordXY()

			If (touchXYRecord <> Null) Then
				If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
				touchRecord.Insert(0, touchXYRecord)
			End If

			keysRecord = FlxG.Touch(0).RecordKeys(keysRecord)
		#End

		mouseRecord = FlxG.Mouse.RecordXY()
		keysRecord = FlxG.Mouse.RecordKeys(keysRecord)

		elapsed = FlxG.Elapsed

		If (keysRecord = Null And mouseRecord = Null And joystickRecord = Null And touchRecord = Null And accelRecord = Null And lastElapsed = elapsed) Then
			frame += 1
			Return
		End If

		_frames[frameCount] = (New FrameRecord()).Create(frame, elapsed, keysRecord, mouseRecord, joystickRecord, touchRecord, accelRecord)
		frame += 1
		frameCount += 1
		lastElapsed = elapsed

		If (frameCount >= _capacity) Then
			_capacity *= 2
			_frames = _frames.Resize(_capacity)
		End If
	End Method

	Method PlayNextFrame:Void()
		FlxG.ResetInput()

		If (_marker >= frameCount) Then
			finished = True
			Return
		End If

		Local frameElapsed:Float = _frames[_marker].elapsed

		If (frameElapsed > FlxG.Elapsed) Then
			Local waitTime:Int = Millisecs() + (frameElapsed - FlxG.Elapsed) * 1000

			While (waitTime > Millisecs())
			Wend
		End If

		FlxG.Elapsed = frameElapsed

		If (_frames[_marker].frame <> frame) Then
			frame += 1
			Return
		End If

		frame += 1

		Local fr:FrameRecord = _frames[_marker]
		_marker += 1

		If (fr.keys <> Null) FlxG.Keys.PlaybackKeys(fr.keys)
		If (fr.mouse <> Null) FlxG.Mouse.PlaybackXY(fr.mouse)

		If (fr.joystick <> Null) Then
			Local i:Int = 0
			Local l:Int = fr.joystick.Length()

			While (i < l)
				FlxG.Joystick(i).PlaybackXYZ(fr.joystick.Get(i))
				i += 1
			Wend
		End If

		If (fr.touch <> Null) Then
			Local i:Int = 0
			Local l:Int = FlxG.TouchCount()
			Local tr:XYRecord

			While (i < l)
				tr = fr.touch.Get(i)
				If (tr = Null) Exit

				FlxG.Touch(i).PlaybackXY(tr)

				i += 1
			Wend
		End If

		If (fr.accel <> Null) FlxG.Accel.PlaybackXYZ(fr.accel)
	End Method

	Method Rewind:Void()
		_marker = 0
		frame = 0
		finished = False
	End Method

Private
	Method _Init:Void()
		_capacity = 100
		_frames = _frames.Resize(_capacity)
		frameCount = 0
	End Method

End Class

Interface FlxReplayListener

	Method OnReplayComplete:Void()

End Interface

Strict

Import mojo

Import flixel.flxg
Import replay.keyrecord
Import replay.xyrecord
Import replay.xyzrecord
Import replay.framerecord

Class FlxReplay

	Field seed:Float

	Field frame:Int

	Field frameCount:Int

	Field elapsed:Float

	Field lastElapsed:Float

	Field finished:Bool

Private
	Field _frames:FrameRecord[]

	Field _capacity:Int

	Field _marker:Int

Public
	Method New()
		seed = 0
		frame = 0
		frameCount = 0
		elapsed = 0
		lastElapsed = 0
		finished = False
		_frames = []
		_capacity = 0
		_marker = 0
	End Method

	Method Destroy:Void()
		If (_frames.Length() = 0) Return

		Local i:Int = frameCount - 1

		While (i >= 0)
			_frames[i].Destroy()
			i -= 1
		Wend

		_frames = []
	End Method

	Method Create:Void(seed:Int)
		Destroy()
		_Init()
		Self.seed = seed
		Rewind()
	End Method

	Method Load:Void(fileContents:String)
		_Init()

		Local lines:String[] = fileContents.Split("~n")

		seed = Int(lines[0])

		Local line:String
		Local i:Int = 1
		Local l:Int = lines.Length()

		While (i < l)
			line = lines[i]

			If (line.Length() > 3) Then
				_frames[frameCount] = (New FrameRecord()).Load(line)
				frameCount += 1

				If (frameCount >= _capacity) Then
					_capacity *= 2
					_frames = _frames.Resize(_capacity)
				End If
			End If

			i += 1
		Wend

		Rewind()
	End Method

	Method Save:String()
		If (frameCount <= 0) Return ""

		Local output:StringStack = New StringStack()
		output.Push(FlxG.GlobalSeed + "~n")

		Local i:Int = 0
		While (i < frameCount)
			_frames[i].Save(output)
			output.Push("~n")
			i += 1
		Wend

		Return output.Join("")
	End Method

	Method RecordFrame:Void()
		Local accelRecord:XYZRecord
		Local joystickRecord:Stack<XYZRecord[]>
		Local touchRecord:Stack<XYRecord>
		Local keysRecord:Stack<KeyRecord>
		Local mouseRecord:XYRecord

		#If TARGET = "html5" Or TARGET = "ios" Or TARGET = "android"
			accelRecord = FlxG.Accel.RecordXYZ()
		#End

		#If TARGET = "xna" Or TARGET = "glfw"
			If (Not FlxG.Mobile) Then
				Local joyCount:Int = FlxG.JoystickCount()
				Local joyXYZRecord:XYZRecord[]

				For Local i:Int = 0 Until joyCount
					joyXYZRecord = FlxG.Joystick(i).RecordXYZ()

					If (joyXYZRecord.Length() > 0) Then
						If (joystickRecord = Null) joystickRecord = New Stack<XYZRecord[]>()
						joystickRecord.Insert(i, joyXYZRecord)
					End If

					keysRecord = FlxG.Joystick(i).RecordKeys(keysRecord)
				Next
			End If
		#End

		#If TARGET = "ios" Or TARGET = "android"
			Local touchCount:Int = FlxG.TouchCount()
			Local touchXYRecord:XYRecord

			For Local i:Int = 0 Until touchCount
				If (i > 0 And Not FlxG.Touch(i).Used) Exit

				touchXYRecord = FlxG.Touch(i).RecordXY()

				If (touchXYRecord <> Null) Then
					If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
					touchRecord.Insert(i, touchXYRecord)
				End If

				keysRecord = FlxG.Touch(i).RecordKeys(keysRecord)
			Next

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

		#ElseIf TARGET = "xna"
			If (Not FlxG.Mobile) Then
				Local touchXYRecord:XYRecord = FlxG.Touch(0).RecordXY()

				If (touchXYRecord <> Null) Then
					If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
					touchRecord.Insert(0, touchXYRecord)
				End If

				keysRecord = FlxG.Touch(0).RecordKeys(keysRecord)
				keysRecord = FlxG.Keys.RecordKeys(keysRecord)
			Else
				Local touchCount:Int = FlxG.TouchCount()
				Local touchXYRecord:XYRecord

				For Local i:Int = 0 Until touchCount
					If (i > 0 And Not FlxG.Touch(i).Used) Exit

					touchXYRecord = FlxG.Touch(i).RecordXY()

					If (touchXYRecord <> Null) Then
						If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
						touchRecord.Insert(i, touchXYRecord)
					End If

					keysRecord = FlxG.Touch(i).RecordKeys(keysRecord)
				Next
			End If

		#Else
			keysRecord = FlxG.Keys.RecordKeys(keysRecord)
		#End

		#If TARGET = "android"
			keysRecord = FlxG.Keys.RecordKeys(keysRecord)
		#End

		#If TARGET = "html5" Or TARGET = "glfw"
			Local touchXYRecord:XYRecord = FlxG.Touch(0).RecordXY()

			If (touchXYRecord <> Null) Then
				If (touchRecord = Null) touchRecord = New Stack<XYRecord>()
				touchRecord.Insert(0, touchXYRecord)
			End If

			keysRecord = FlxG.Touch(0).RecordKeys(keysRecord)
		#End

		mouseRecord = FlxG.Mouse.RecordXY()
		keysRecord = FlxG.Mouse.RecordKeys(keysRecord)

		elapsed = FlxG.Elapsed

		If (keysRecord = Null And mouseRecord = Null And joystickRecord = Null And touchRecord = Null And accelRecord = Null And lastElapsed = elapsed) Then
			frame += 1
			Return
		End If

		_frames[frameCount] = (New FrameRecord()).Create(frame, elapsed, keysRecord, mouseRecord, joystickRecord, touchRecord, accelRecord)
		frame += 1
		frameCount += 1
		lastElapsed = elapsed

		If (frameCount >= _capacity) Then
			_capacity *= 2
			_frames = _frames.Resize(_capacity)
		End If
	End Method

	Method PlayNextFrame:Void()
		FlxG.ResetInput()

		If (_marker >= frameCount) Then
			finished = True
			Return
		End If

		Local frameElapsed:Float = _frames[_marker].elapsed

		If (FlxG.Elapsed < frameElapsed) Then
			Local waitTime:Int = Millisecs() + (frameElapsed - FlxG.Elapsed) * 1000

			While (waitTime > Millisecs())
			Wend

			FlxG.Elapsed = frameElapsed
		Else
			FlxG.Elapsed -= (FlxG.Elapsed - frameElapsed)
		End If

		If (_frames[_marker].frame <> frame) Then
			frame += 1
			Return
		End If

		frame += 1

		Local fr:FrameRecord = _frames[_marker]
		_marker += 1

		If (fr.keys <> Null) FlxG.Keys.PlaybackKeys(fr.keys)
		If (fr.mouse <> Null) FlxG.Mouse.PlaybackXY(fr.mouse)

		If (fr.joystick <> Null) Then
			Local i:Int = 0
			Local l:Int = fr.joystick.Length()

			While (i < l)
				FlxG.Joystick(i).PlaybackXYZ(fr.joystick.Get(i))
				i += 1
			Wend
		End If

		If (fr.touch <> Null) Then
			Local i:Int = 0
			Local l:Int = FlxG.TouchCount()
			Local tr:XYRecord

			While (i < l)
				tr = fr.touch.Get(i)
				If (tr = Null) Exit

				FlxG.Touch(i).PlaybackXY(tr)

				i += 1
			Wend
		End If

		If (fr.accel <> Null) FlxG.Accel.PlaybackXYZ(fr.accel)
	End Method

	Method Rewind:Void()
		_marker = 0
		frame = 0
		finished = False
	End Method

Private
	Method _Init:Void()
		_capacity = 100
		_frames = _frames.Resize(_capacity)
		frameCount = 0
	End Method

End Class

Interface FlxReplayListener

	Method OnReplayComplete:Void()

End Interface


Small fix for OnRender/OnUpdate

Small fix for OnRender/OnUpdate

Strict

Import mojo

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

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updatesCounter:FlxFPSCounter

	Field _rendersCounter:FlxFPSCounter

	Field _updatesLoopUsed:Bool

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

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

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

		_state = Null

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

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

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

		_updatesCounter = New FlxFPSCounter()
		_rendersCounter = New FlxFPSCounter()
	End Method

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

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

	Method OnUpdate:Int()
		If (_rendersCounter.FPS >= 0 And _rendersCounter.FPS < 30) Then
			_updatesCounter.Update()

			FlxG.Elapsed = FlxG.TimeScale * (1.0 / _updatesCounter.FPS)
			_Step()

			_updatesLoopUsed = True
		Else
			_updatesLoopUsed = False
		End If
		Return 0
	End Method

	Method OnRender:Int()
		If (Not _replaying) Then
			_rendersCounter.Update()
		End If

		If (Not _updatesLoopUsed) Then
			FlxG.Elapsed = FlxG.TimeScale * (1.0 / _rendersCounter.FPS)
			_Step()
		End If

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

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

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

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

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

			FlxG._CurrentCameraID = i

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

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

			i+=1
		Wend

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

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

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

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

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

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

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

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

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.Mouse.Draw()

		#If CONFIG = "debug"
			SetColor(255, 255, 255)
			DrawText("FPS: " + _renderFPS, 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 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

			_UpdateSoundTray()
		#End

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

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

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

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

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

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

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

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

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

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

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

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

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

		If (_recording) Then
			_replay.RecordFrame()

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

		_Update()

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

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

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

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

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

				'TODO Save sound settings
			End If
		End If
	End Method

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

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

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

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

		_step = 1000.0 / FlxG.Framerate

		_updatesCounter.Reset()
		_rendersCounter.Reset()
	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

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastFPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (FPS < 0) Then
			_lastUpdateTime = Millisecs()
			FPS = FlxG.Framerate
			_lastFPSCounter = FPS
		Else
			If (Millisecs() - _lastUpdateTime > 1000) Then
				If (Abs(_lastFPSCounter - _FPSCounter) < 10) Then
					FPS = _FPSCounter
				End If

				_lastFPSCounter = _FPSCounter
				_FPSCounter = 0
				_lastUpdateTime = Millisecs()
			Else
				_FPSCounter += 1
			End If
		End If
	End Method

	Method Reset:Void()
		FPS = -1
		_FPSCounter = 0
		_lastFPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class

Strict

Import mojo

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

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updatesCounter:FlxFPSCounter

	Field _rendersCounter:FlxFPSCounter

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

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

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

		_state = Null

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

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

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

		_updatesCounter = New FlxFPSCounter()
		_rendersCounter = New FlxFPSCounter()
	End Method

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

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

	Method OnUpdate:Int()
		If (_rendersCounter.FPS >= 0 And _rendersCounter.FPS < 30) Then
			_updatesCounter.Update()

			FlxG.Elapsed = FlxG.TimeScale * (1.0 / _updatesCounter.FPS)
			_Step()
		End If
		Return 0
	End Method

	Method OnRender:Int()
		If (Not _replaying) Then
			_rendersCounter.Update()
		End If

		If (_rendersCounter.FPS >= 30) Then
			FlxG.Elapsed = FlxG.TimeScale * (1.0 / _rendersCounter.FPS)
			_Step()
		End If

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

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

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

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

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

			FlxG._CurrentCameraID = i

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

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

			i+=1
		Wend

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

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

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

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

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

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

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

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

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.Mouse.Draw()

		#If CONFIG = "debug"
			SetColor(255, 255, 255)
			DrawText("FPS: " + _renderFPS, 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 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

			_UpdateSoundTray()
		#End

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

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

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

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

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

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

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

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

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

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

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

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

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

		If (_recording) Then
			_replay.RecordFrame()

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

		_Update()

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

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

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

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

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

				'TODO Save sound settings
			End If
		End If
	End Method

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

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

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

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

		_step = 1000.0 / FlxG.Framerate

		_updatesCounter.Reset()
		_rendersCounter.Reset()
	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

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastFPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (FPS < 0) Then
			_lastUpdateTime = Millisecs()
			FPS = FlxG.Framerate
			_lastFPSCounter = FPS
		Else
			If (Millisecs() - _lastUpdateTime > 1000) Then
				If (Abs(_lastFPSCounter - _FPSCounter) < 10) Then
					FPS = _FPSCounter
				End If

				_lastFPSCounter = _FPSCounter
				_FPSCounter = 0
				_lastUpdateTime = Millisecs()
			Else
				_FPSCounter += 1
			End If
		End If
	End Method

	Method Reset:Void()
		FPS = -1
		_FPSCounter = 0
		_lastFPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class


Fixed FlxInvaders

Fixed FlxInvaders

Strict

Import flixel

Import playership
Import alien

Class PlayState Extends FlxState Implements FlxQuadTreeOverlapNotifyCallback

	Global _CLASS:FlxClass = New PlayStateClass()

	Field caption:FlxText

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

	Field vsPlayerBullets:FlxGroup
	Field vsAlienBullets:FlxGroup

	Field scoresLabel:FlxText

	Method Create:Void()
		If (FlxG.scores.Length() = 0) Then
			FlxG.scores.Insert(0, "WELCOME TO FLX INVADERS")
		End If

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

		player = New PlayerShip(playerBullets)
		Add(player)

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

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

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

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

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

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

		StringObject

		scoresLabel = New FlxText(4, 4, FlxG.width - 8, FlxG.scores.Get(0))
		scoresLabel.Alignment = FlxText.ALIGN_CENTER
		Add(scoresLabel)
	End Method

	Method Update:Void()
		If (FlxG.keys.JustPressed(KEY_ESCAPE)) Error ""
		If (MouseHit()) HideMouse()

		For Local alien:FlxBasic = Eachin aliens
			If (Alien(alien).SwitchDirectionNeeded()) Then
				aliens.CallAll(Alien.SwitchDirection)
				Exit
			End If
		Next

		If (Not player.exists) Then
			FlxG.scores.Set(0, "YOU LOST")
			scoresLabel.Text = FlxG.scores.Get(0)
			FlxG.ResetState()

		ElseIf (aliens.GetFirstExtant() = Null)
			FlxG.scores.Set(0, "YOU WON")
			scoresLabel.Text = FlxG.scores.Get(0)
			FlxG.ResetState()
		End If

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

		Super.Update()
	End Method

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

	Method GetClass:FlxClass()
		Return _CLASS
	End Method

End Class

Class PlayStateClass Implements FlxClass

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

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

End Class

Strict

Import flixel

Import playership
Import alien

Class PlayState Extends FlxState Implements FlxQuadTreeOverlapNotifyCallback

	Global _CLASS:FlxClass = New PlayStateClass()

	Field caption:FlxText

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

	Field vsPlayerBullets:FlxGroup
	Field vsAlienBullets:FlxGroup

	Method Create:Void()
		If (FlxG.scores.Length() = 0) Then
			FlxG.scores.Insert(0, New FlxString("WELCOME TO FLX INVADERS"))
		End If

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

		player = New PlayerShip(playerBullets)
		Add(player)

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

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

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

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

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

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

		Local t:FlxText = New FlxText(4, 4, FlxG.width - 8)
		t.Text = FlxG.scores.Get(0)
		t.Alignment = FlxText.ALIGN_CENTER
		Add(t)
	End Method

	Method Update:Void()
		If (FlxG.keys.JustPressed(KEY_ESCAPE)) Error ""
		If (MouseHit()) HideMouse()

		For Local alien:FlxBasic = Eachin aliens
			If (Alien(alien).SwitchDirectionNeeded()) Then
				aliens.CallAll(Alien.SwitchDirection)
				Exit
			End If
		Next

		If (Not player.exists) Then
			FlxG.scores.Get(0).FromString("YOU LOST")
			FlxG.ResetState()

		Elseif (aliens.GetFirstExtant() = Null)
			FlxG.scores.Get(0).FromString("YOU WON")
			FlxG.ResetState()
		End If

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

		Super.Update()
	End Method

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

	Method GetClass:FlxClass()
		Return _CLASS
	End Method

End Class

Class PlayStateClass Implements FlxClass

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

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

End Class


use KeyHit to detect very fast key events

Strict

Private

Import bono
Import mojo.input

Public

Class KeyEmitter Implements Updateable, Suspendable
    Private

    Field keyboardEnabled:Bool
    Field event:KeyEvent = New KeyEvent()
    Field keyStates:Int[255]
    Field dirty:Bool

    Const OFF:Int  = %000
    Const HIT:Int  = $001
    Const DOWN:Int = $010
    Const UP:Int   = %100

    Public

    Field active:Bool = True
    Field handler:Keyhandler
    Field showKeyboard:Bool = True

    Method OnResume:Void()
    End

    Method OnSuspend:Void()
        Reset()
    End

    Method OnUpdate:Void(deltatimer:DeltaTimer)
        If active And handler
            If showKeyboard
                EnableKeyboard()
            Else
                DisableKeyboard()
            End

            UpdateKeykeyStates()
            DispatchKeyEvents()
        Else
            Reset()
            DisableKeyboard()
        End
    End

    Private

    Method DisableKeyboard:Void()
        If Not keyboardEnabled Then Return
        keyboardEnabled = False

        input.DisableKeyboard()
    End

    Method EnableKeyboard:Void()
        If keyboardEnabled Then Return
        keyboardEnabled = True

        input.EnableKeyboard()
    End

    Method Reset:Void()
        If Not dirty Then Return
        dirty = False

        For Local i:Int = 0 Until keyStates.Length()
            keyStates[i] = OFF
        End
    End

    Method UpdateKeykeyStates:Void()
        dirty = True

        For Local i:Int = 0 Until keyStates.Length()
            If keyStates[i] = OFF
                If KeyHit(i) Then keyStates[i] = DOWN | HIT
            ElseIf keyStates[i] & HIT
                If KeyDown(i)
                    keyStates[i] = DOWN
                Else
                    keyStates[i] = UP
                End
            Else
                If keyStates[i] & UP
                    keyStates[i] = OFF
                Else
                    If Not KeyDown(i) Then keyStates[i] = UP
                End
            End
        End
    End

    Method DispatchKeyEvents:Void()
        For Local i:Int = 0 Until keyStates.Length()
            If keyStates[i] = OFF Then Continue

            event.code = i
            If keyStates[i] & HIT  Then handler.OnKeyDown(event)
            If keyStates[i] & DOWN Then handler.OnKeyPress(event)
            If keyStates[i] & UP   Then handler.OnKeyUp(event)
        End
    End
End

Strict

Private

Import bono
Import mojo.input

Public

Class KeyEmitter Implements Updateable, Suspendable
    Private

    Field keyboardEnabled:Bool
    Field event:KeyEvent = New KeyEvent()
    Field lastMode:Bool[255]
    Field dirty:Bool

    Const UP:Int = 0
    Const DOWN:Int = 1
    Const PRESS:Int = 2

    Public

    Field active:Bool = True
    Field handler:Keyhandler
    Field showKeyboard:Bool = True

    Method OnResume:Void()
    End

    Method OnSuspend:Void()
        Reset()
    End

    Method OnUpdate:Void(deltatimer:DeltaTimer)
        If showKeyboard
            EnableKeyboard()
        Else
            DisableKeyboard()
        End

        If active And handler
            ProcessKeys()
        Else
            Reset()
        End
    End

    Private

    Method DisableKeyboard:Void()
        If Not keyboardEnabled Then Return
        keyboardEnabled = False

        input.DisableKeyboard()
    End

    Method EnableKeyboard:Void()
        If keyboardEnabled Then Return
        keyboardEnabled = True

        input.EnableKeyboard()
    End

    Method Reset:Void()
        If Not dirty Then Return
        dirty = False

        For Local i:Int = 0 Until lastMode.Length()
            lastMode[i] = False
        End
    End

    Method ProcessKeys:Void()
        Local mode:Bool
        dirty = True

        For Local i:Int = 0 Until lastMode.Length()
            mode = (KeyDown(i) = 1)

            If mode = lastMode[i]
                If mode Then DispatchEvent(i, PRESS)
            Else
                lastMode[i] = mode
                If mode
                    DispatchEvent(i, DOWN)
                Else
                    DispatchEvent(i, UP)
                End
            End
        End
    End

    Method DispatchEvent:Void(code:Int, mode:Int)
        event.code = code

        Select mode
        Case UP
            handler.OnKeyUp(event)
        Case DOWN
            handler.OnKeyDown(event)
        Case PRESS
            handler.OnKeyPress(event)
        End
    End
End


Added new GetFirstChildByName method.

Added new GetFirstChildByName method.

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

' Diddy XML parser
' This is a very simple XML parser, and expects a document to be as close to well-formed as possible.
' The only real leniency it has is that attributes may be defined without surrounding quotes/double quotes.
' All XML characters should be escaped in strings. > < etc.
' Currently it supports regular tag and attribute definitions, processing instructions, and skipping comments.
' For now, exported indentation is hardcoded to 2 spaces.
' Written from scratch, using as little string manipulation as possible.

Import functions
Import collections

' note that we don't define a separate method for checking whitespace, to improve performance on android
Class XMLParser
	Field str:String

	' find a string, ignoring quoted strings
	Method FindUnquoted:Int(findit:String, start:Int)
		' look for the string
		Local a:Int = str.Find(findit, start)

		' look for a single or double quote
		Local singleQuote:Int = str.Find("'", start)
		Local doubleQuote:Int = str.Find("~q", start)
		Local quoteToFind:String = ""
		Local b:Int = -1
		If singleQuote >= 0 And doubleQuote < 0 Or singleQuote >= 0 And singleQuote < doubleQuote Then
			quoteToFind = "'"
			b = singleQuote
		ElseIf doubleQuote >= 0 And singleQuote < 0 Or doubleQuote >= 0 And doubleQuote < singleQuote Then
			quoteToFind = "~q"
			b = doubleQuote
		End

		' while there's a quote to check
		While b >= 0 And a >= 0 And a > b
			' find the ending quote
			b = str.Find(quoteToFind, b+1)

			' if not found, the quote doesn't end, so error
			If b < 0 Then AssertError("Unclosed quote detected.")

			' find the next instance after b
			a = str.Find(findit, b+1)

			' look for another starting quote
			singleQuote = str.Find("'", b+1)
			doubleQuote = str.Find("~q", b+1)
			b = -1
			If singleQuote >= 0 And doubleQuote < 0 Or singleQuote >= 0 And singleQuote < doubleQuote Then
				quoteToFind = "'"
				b = singleQuote
			ElseIf doubleQuote >= 0 And singleQuote < 0 Or doubleQuote >= 0 And doubleQuote < singleQuote Then
				quoteToFind = "~q"
				b = doubleQuote
			End
		End
		' done, so return a (it may be negative if not found)
		Return a
	End

	' get the contents of a tag (given start and end)
	Method GetTagContents:XMLElement(startIndex:Int, endIndex:Int)
		' trim trailing whitespace
		While endIndex > startIndex And (str[endIndex]=ASC_SPACE Or str[endIndex]=ASC_TAB Or str[endIndex]=ASC_LF Or str[endIndex]=ASC_CR)
			endIndex += 1
		End
		' die if empty tag
		If startIndex = endIndex Then AssertError("Empty tag detected.")
		' our element
		Local e:XMLElement = New XMLElement
		Local a:Int, singleQuoted:Bool, doubleQuoted:Bool, key:String, value:String

		' trim leading whitespace
		While str[startIndex]=ASC_SPACE Or str[startIndex]=ASC_TAB Or str[startIndex]=ASC_LF Or str[startIndex]=ASC_CR
			startIndex += 1
		End

		' get the name
		a = startIndex
		While a < endIndex
			If str[a]=ASC_SPACE Or str[a]=ASC_TAB Or str[a]=ASC_LF Or str[a]=ASC_CR Or a = endIndex-1 Then
				If a = endIndex-1 Then
					e.name = str[startIndex..endIndex]
				Else
					e.name = str[startIndex..a]
				End
				a += 1
				Exit
			End
			a += 1
		End
		startIndex = a

		' TODO: validate tag name is alphanumeric
		' if no name, die
		If e.name = "" Then AssertError("Error reading tag name.")

		' loop on all tokens
		While startIndex < endIndex
			' trim leading whitespace
			While startIndex < endIndex And (str[startIndex]=ASC_SPACE Or str[startIndex]=ASC_TAB Or str[startIndex]=ASC_LF Or str[startIndex]=ASC_CR)
				startIndex += 1
			End

			' clear check variables
			singleQuoted = False
			doubleQuoted = False
			key = ""
			value = ""

			' find the key
			a = startIndex
			While a < endIndex
				If str[a] = ASC_EQUALS Or str[a]=ASC_SPACE Or str[a]=ASC_TAB Or str[a]=ASC_LF Or str[a]=ASC_CR Or a = endIndex-1 Then
					If a=endIndex-1 Then
						key = str[startIndex..endIndex]
					Else
						key = str[startIndex..a]
					End
					a += 1
					Exit
				End
				a += 1
			End
			startIndex = a

			' if the key is empty, there was an error (unless we've hit the end of the string)
			If key = "" Then
				If a < endIndex Then
					AssertError("Error reading attribute key.")
				Else
					Exit
				End
			End

			' if it stopped on an equals, get the value
			If str[a-1] = ASC_EQUALS Then
				singleQuoted = False
				doubleQuoted = False
				While a < endIndex
					' check if it's a single quote
					If str[a] = ASC_SINGLE_QUOTE And Not doubleQuoted Then
						' if this is the first index, mark it as quoted
						If a = startIndex Then
							singleQuoted = True
						' otherwise, if we're not quoted at all, die
						ElseIf Not singleQuoted And Not doubleQuoted Then
							AssertError("Unexpected single quote detected in attribute value.")
						Else
							' we must be ending the quote here, so grab it and break out
							singleQuoted = False
							value = str[startIndex+1..a]
							a += 1
							Exit
						End

					' check if it's a double quote
					ElseIf str[a] = ASC_DOUBLE_QUOTE And Not singleQuoted Then
						' if this is the first index, mark it as quoted
						If a = startIndex Then
							doubleQuoted = True
						' otherwise, if we're not quoted at all, die
						ElseIf Not singleQuoted And Not doubleQuoted Then
							AssertError("Unexpected double quote detected in attribute value.")
						Else
							' we must be ending the quote here, so break out
							doubleQuoted = False
							value = str[startIndex+1..a]
							a += 1
							Exit
						End

					' should we be ending the attribute?
					ElseIf a = endIndex-1 Or (Not singleQuoted And Not doubleQuoted And (str[a]=ASC_SPACE Or str[a]=ASC_TAB Or str[a]=ASC_LF Or str[a]=ASC_CR))
						If a=endIndex-1 Then
							value = str[startIndex..endIndex]
						Else
							value = str[startIndex..a]
						End
						a += 1
						Exit
					End
					a += 1
				End
				startIndex = a
				value = UnescapeXMLString(value)

				If singleQuoted Or doubleQuoted Then AssertError("Unclosed quote detected.")
			End

			' set the attribute
			e.SetAttribute(key, value)

			If a >= endIndex Then Exit
		End
		Return e
	End

	Method ParseFile:XMLDocument(filename:String)
		Return ParseString(LoadString(filename))
	End

	' parses an xml doc, currently doesn't support nested PI or prolog
	Method ParseString:XMLDocument(str:String)
		Self.str = str

		Local doc:XMLDocument = New XMLDocument
		Local elements:ArrayList<XMLElement> = New ArrayList<XMLElement>
		Local thisE:XMLElement = Null, newE:XMLElement = Null
		Local index:Int = 0, a:Int, b:Int, c:Int, nextIndex:Int

		' find first opening tag
		a = str.Find("<", index)
		While a >= index
			' read text between tags
			If a > index And str[index..a].Trim() <> "" Then
				If thisE <> Null Then
					thisE.value += UnescapeXMLString(str[index..a].Trim())
				Else
					AssertError("Loose text outside of any tag!")
				End
			End
			' check for PI
			If str[a+1] = ASC_QUESTION Then
				' die if the PI is inside the document
				If thisE <> Null Then AssertError("Processing instruction detected inside main document tag.")
				' create PI element up until next unquoted ?> after a
				nextIndex = FindUnquoted("?>", a+2)
				newE = GetTagContents(a+2, nextIndex)
				newE.pi = True
				doc.pi.Add(newE)
				newE = Null
				nextIndex += 2
			' check for prolog
			ElseIf str[a+1] = ASC_EXCLAMATION Then
				' if the next two chars are -- it's a comment
				If str[a+2] = ASC_HYPHEN And str[a+3] = ASC_HYPHEN Then
					' ignore everything until the next -->
					nextIndex = str.Find("-->", a+4)
					' if we couldn't find a comment end, die
					If nextIndex < 0 Then AssertError("Unclosed comment detected.")
					' XML specifications say that ---> is invalid
					If str[nextIndex-1] = ASC_HYPHEN Then AssertError("Invalid comment close detected (too many hyphens).")
					nextIndex += 3

				' if the next seven chars are [CDATA[ it's a cdata block
				ElseIf str.Find("[CDATA[", a+2) = a+2 Then
					' die if the CDATA is outside the document
					If thisE = Null Then AssertError("CDATA detected outside main document tag.")
					nextIndex = str.Find("]]>", a+9)
					' die if it doesn't end
					If nextIndex < 0 Then AssertError("Unclosed CDATA tag detected.")
					newE = New XMLElement
					newE.value = str[a+9..nextIndex]
					newE.cdata = True
					thisE.AddChild(newE)
					newE = Null
					nextIndex += 3

				' if the next seven chars are DOCTYPE it's a dtd tag
				ElseIf str.Find("DOCTYPE", a+2) = a+2 Then
					' die if the doctype is inside the document
					If thisE <> Null Then AssertError("DOCTYPE detected inside main document tag.")
					nextIndex = FindUnquoted(">", a+9)
					newE = GetTagContents(a+9, nextIndex)
					newE.prolog = True
					doc.prologs.Add(newE)
					newE = Null
					nextIndex += 1

				' don't know!
				Else
					AssertError("Unknown prolog detected.")
				End

			' check for closing tag
			ElseIf str[a+1] = ASC_SLASH Then
				' if no current element, die
				If thisE = Null Then AssertError("Closing tag found outside main document tag.")
				' find the next >
				nextIndex = str.Find(">", a+2)
				' if not found, the closing tag is broken
				If nextIndex < 0 Then AssertError("Incomplete closing tag detected.")
				' check that the tag name matches
				If str[a+2..nextIndex].Trim() <> thisE.name Then AssertError("Closing tag ~q"+str[a+2..nextIndex].Trim()+"~q does not match opening tag ~q"+thisE.name+"~q")
				If Not elements.IsEmpty() Then
					thisE = elements.RemoveLast()
				Else
					doc.root = thisE
					Exit
				End
				nextIndex += 1

			' check for opening tag
			Else
				' look for the end of the tag, and whether it's self-closing
				b = FindUnquoted("/>", a+1)
				c = FindUnquoted(">", a+1)
				' if we couldn't find either
				If c < 0 Then
					AssertError("Incomplete opening tag detected.")

				' if it's not self-closing
				ElseIf b < 0 Or c < b Then
					' get the new one
					newE = GetTagContents(a+1, c)
					If thisE <> Null Then
						' push the current element to the stack
						elements.AddLast(thisE)
						' add it as a child element
						thisE.AddChild(newE)
					End
					thisE = newE
					newE = Null
					nextIndex = c+1
				' it's self-closing
				Else
					newE = GetTagContents(a+1, b)
					If thisE <> Null Then
						thisE.AddChild(newE)
					Else
						doc.root = newE
						Exit
					End
					newE = Null
					nextIndex = b+2
				End
			End
			index = nextIndex
			a = str.Find("<", index)
		End
		If doc.root = Null Then AssertError("Error parsing XML: no document tag found.")
		Return doc
	End
End

Class XMLDocument
Private
	Field xmlVersion:String = "1.0"
	Field xmlEncoding:String = "UTF-8"
	Field root:XMLElement
	Field pi:ArrayList<XMLElement> = New ArrayList<XMLElement>
	Field prologs:ArrayList<XMLElement> = New ArrayList<XMLElement>

Public

' Constructors
	Method New(rootName:String="")
		If rootName <> "" Then root = New XMLElement(rootName)
	End

	Method New(root:XMLElement)
		Self.root = root
	End

' Methods
	Method ExportString:String(formatXML:Bool = True)
		' we start with the xml instruction
		Local output:String = "<?xml version=~q"+xmlVersion+"~q encoding=~q"+xmlEncoding+"~q?>"
		If formatXML Then output += "~n"
		' root node
		output += root.ToString(formatXML) + "~n"
		' done!
		Return output
	End

	Method SaveFile:Void(filename:String)
		' TODO when file IO is ready!
	End

' Properties
	Method Root:XMLElement() Property
		Return root
	End

	Method Prologs:ArrayList<XMLElement>() Property
		Return prologs
	End

	Method ProcessingInstructions:ArrayList<XMLElement>() Property
		Return pi
	End

	Method Version:String() Property
		Return xmlVersion
	End

	Method Encoding:String() Property
		Return xmlEncoding
	End
End

Class XMLAttribute
Private
	Field name:String
	Field value:String

' Constructors
	Method New(name:String, value:String)
		Self.name = name
		Self.value = value
	End
End

Class XMLElement
Private
	Field openTagStart:String = "<"
	Field openTagEnd:String = ">"
	Field selfCloseEnd:String = " />"
	Field closeTagStart:String = "</"
	Field closeTagEnd:String = ">"

	Field name:String
	Field attributes:ArrayList<XMLAttribute> = New ArrayList<XMLAttribute>
	Field children:ArrayList<XMLElement> = New ArrayList<XMLElement>
	Field value:String
	Field parent:XMLElement

	Field pi:Bool
	Field prolog:Bool
	Field cdata:Bool

Public
' Constructors
	Method New()
	End

	Method New(name:String, parent:XMLElement = Null)
		Self.parent = parent
		Self.name = name
		If parent <> Null Then parent.children.Add(Self)
	End

' Methods
	Method IsProcessingInstruction:Bool()
		Return pi
	End

	Method IsProlog:Bool()
		Return prolog
	End

	Method IsCharacterData:Bool()
		Return cdata
	End

	' avoid using this method if you can, because you should try not to have "floating" elements
	Method AddChild:Void(child:XMLElement)
		If children.Contains(child) Return
		children.Add(child)
		child.parent = Self
	End

	Method HasAttribute:Bool(name:String)
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then Return True
		Next
		Return False
	End

	Method GetAttribute:String(name:String, defaultValue:String = "")
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then Return att.value
		Next
		Return defaultValue
	End

	Method SetAttribute:String(name:String, value:String)
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then
				Local old:String = att.value
				att.value = value
				Return old
			End
		Next
		attributes.Add(New XMLAttribute(name, value))
		Return ""
	End

	Method ClearAttribute:String(name:String)
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then
				attributes.Remove(att)
				Return att.value
			End
		Next
		Return ""
	End

	Method Dispose:Void(removeSelf:Bool = True)
		' dispose children
		Local en:IEnumerator<XMLElement> = children.Enumerator()
		While en.HasNext()
			Local element:XMLElement = en.NextObject()
			element.Dispose(False)
			en.Remove()
		End
		' remove self from parent if this is not recursing
		If removeSelf And parent <> Null Then parent.children.Remove(Self)
		' clear out the parent
		parent = Null
	End

	Method ToString:String(formatXML:Bool=True, indentation:Int=0)
		Local rv:String = ""
		If formatXML Then
			For Local i% = 0 Until indentation
				rv += "  "
			Next
		End
		rv += openTagStart + name
		If Not attributes.IsEmpty() Then
			For Local i% = 0 Until attributes.Size
				Local att:XMLAttribute = attributes.Get(i)
				rv += " " + att.name + "=~q" + EscapeXMLString(att.value) + "~q"
			Next
		End
		If children.IsEmpty() Then
			Local esc:String = EscapeXMLString(value.Trim())
			If esc.Length() = 0
				rv += selfCloseEnd
			Else
				rv += openTagEnd + value.Trim() + closeTagStart + name + closeTagEnd
			End
		Else
			rv += openTagEnd
			If formatXML Then
				rv += "~n"
			End
			For Local i% = 0 Until children.Size
				rv += children.Get(i).ToString(formatXML, indentation + 1)
			End
			Local esc:String = EscapeXMLString(value.Trim())
			If esc.Length() > 0 Then
				If Not formatXML Then
					rv += esc
				Else
					rv += "~n" + esc + "~n"
				End
			End
			If formatXML Then
				For Local i% = 0 Until indentation
					rv += "  "
				Next
			End
			rv += closeTagStart + name + closeTagEnd
		End
		If formatXML Then
			rv += "~n"
		End
		Return rv
	End

	Method GetChildrenByName:ArrayList<XMLElement>(findName:String)
		Local rv:ArrayList<XMLElement> = New ArrayList<XMLElement>
		For Local element:XMLElement = EachIn children
			If element.name = findName Then rv.Add(element)
		Next
		Return rv
	End

	Method GetFirstChildByName:XMLElement(findName:String)
		For Local element:XMLElement = EachIn children
			If element.name = findName Then Return element
		Next
		Return Null
	End

' Properties
	Method Children:ArrayList<XMLElement>() Property
		Return children
	End

	Method Parent:XMLElement() Property
		Return parent
	End

	Method Name:String() Property
		Return name
	End

	Method Value:String() Property
		Return value
	End
End

Function EscapeXMLString:String(str:String)
	str = str.Replace("&", "&")
	str = str.Replace("<", "<")
	str = str.Replace(">", ">")
	str = str.Replace("'", "'")
	str = str.Replace("~q", """)
	Return str
End

Function UnescapeXMLString:String(str:String)
	str = str.Replace(""", "~q")
	str = str.Replace("'", "'")
	str = str.Replace(">", ">")
	str = str.Replace("<", "<")
	str = str.Replace("&", "&")
	Return str
End

' Diddy XML parser
' This is a very simple XML parser, and expects a document to be as close to well-formed as possible.
' The only real leniency it has is that attributes may be defined without surrounding quotes/double quotes.
' All XML characters should be escaped in strings. > < etc.
' Currently it supports regular tag and attribute definitions, processing instructions, and skipping comments.
' For now, exported indentation is hardcoded to 2 spaces.
' Written from scratch, using as little string manipulation as possible.

Import functions
Import collections

' note that we don't define a separate method for checking whitespace, to improve performance on android
Class XMLParser
	Field str:String

	' find a string, ignoring quoted strings
	Method FindUnquoted:Int(findit:String, start:Int)
		' look for the string
		Local a:Int = str.Find(findit, start)

		' look for a single or double quote
		Local singleQuote:Int = str.Find("'", start)
		Local doubleQuote:Int = str.Find("~q", start)
		Local quoteToFind:String = ""
		Local b:Int = -1
		If singleQuote >= 0 And doubleQuote < 0 Or singleQuote >= 0 And singleQuote < doubleQuote Then
			quoteToFind = "'"
			b = singleQuote
		ElseIf doubleQuote >= 0 And singleQuote < 0 Or doubleQuote >= 0 And doubleQuote < singleQuote Then
			quoteToFind = "~q"
			b = doubleQuote
		End

		' while there's a quote to check
		While b >= 0 And a >= 0 And a > b
			' find the ending quote
			b = str.Find(quoteToFind, b+1)

			' if not found, the quote doesn't end, so error
			If b < 0 Then AssertError("Unclosed quote detected.")

			' find the next instance after b
			a = str.Find(findit, b+1)

			' look for another starting quote
			singleQuote = str.Find("'", b+1)
			doubleQuote = str.Find("~q", b+1)
			b = -1
			If singleQuote >= 0 And doubleQuote < 0 Or singleQuote >= 0 And singleQuote < doubleQuote Then
				quoteToFind = "'"
				b = singleQuote
			ElseIf doubleQuote >= 0 And singleQuote < 0 Or doubleQuote >= 0 And doubleQuote < singleQuote Then
				quoteToFind = "~q"
				b = doubleQuote
			End
		End
		' done, so return a (it may be negative if not found)
		Return a
	End

	' get the contents of a tag (given start and end)
	Method GetTagContents:XMLElement(startIndex:Int, endIndex:Int)
		' trim trailing whitespace
		While endIndex > startIndex And (str[endIndex]=ASC_SPACE Or str[endIndex]=ASC_TAB Or str[endIndex]=ASC_LF Or str[endIndex]=ASC_CR)
			endIndex += 1
		End
		' die if empty tag
		If startIndex = endIndex Then AssertError("Empty tag detected.")
		' our element
		Local e:XMLElement = New XMLElement
		Local a:Int, singleQuoted:Bool, doubleQuoted:Bool, key:String, value:String

		' trim leading whitespace
		While str[startIndex]=ASC_SPACE Or str[startIndex]=ASC_TAB Or str[startIndex]=ASC_LF Or str[startIndex]=ASC_CR
			startIndex += 1
		End

		' get the name
		a = startIndex
		While a < endIndex
			If str[a]=ASC_SPACE Or str[a]=ASC_TAB Or str[a]=ASC_LF Or str[a]=ASC_CR Or a = endIndex-1 Then
				If a = endIndex-1 Then
					e.name = str[startIndex..endIndex]
				Else
					e.name = str[startIndex..a]
				End
				a += 1
				Exit
			End
			a += 1
		End
		startIndex = a

		' TODO: validate tag name is alphanumeric
		' if no name, die
		If e.name = "" Then AssertError("Error reading tag name.")

		' loop on all tokens
		While startIndex < endIndex
			' trim leading whitespace
			While startIndex < endIndex And (str[startIndex]=ASC_SPACE Or str[startIndex]=ASC_TAB Or str[startIndex]=ASC_LF Or str[startIndex]=ASC_CR)
				startIndex += 1
			End

			' clear check variables
			singleQuoted = False
			doubleQuoted = False
			key = ""
			value = ""

			' find the key
			a = startIndex
			While a < endIndex
				If str[a] = ASC_EQUALS Or str[a]=ASC_SPACE Or str[a]=ASC_TAB Or str[a]=ASC_LF Or str[a]=ASC_CR Or a = endIndex-1 Then
					If a=endIndex-1 Then
						key = str[startIndex..endIndex]
					Else
						key = str[startIndex..a]
					End
					a += 1
					Exit
				End
				a += 1
			End
			startIndex = a

			' if the key is empty, there was an error (unless we've hit the end of the string)
			If key = "" Then
				If a < endIndex Then
					AssertError("Error reading attribute key.")
				Else
					Exit
				End
			End

			' if it stopped on an equals, get the value
			If str[a-1] = ASC_EQUALS Then
				singleQuoted = False
				doubleQuoted = False
				While a < endIndex
					' check if it's a single quote
					If str[a] = ASC_SINGLE_QUOTE And Not doubleQuoted Then
						' if this is the first index, mark it as quoted
						If a = startIndex Then
							singleQuoted = True
						' otherwise, if we're not quoted at all, die
						ElseIf Not singleQuoted And Not doubleQuoted Then
							AssertError("Unexpected single quote detected in attribute value.")
						Else
							' we must be ending the quote here, so grab it and break out
							singleQuoted = False
							value = str[startIndex+1..a]
							a += 1
							Exit
						End

					' check if it's a double quote
					ElseIf str[a] = ASC_DOUBLE_QUOTE And Not singleQuoted Then
						' if this is the first index, mark it as quoted
						If a = startIndex Then
							doubleQuoted = True
						' otherwise, if we're not quoted at all, die
						ElseIf Not singleQuoted And Not doubleQuoted Then
							AssertError("Unexpected double quote detected in attribute value.")
						Else
							' we must be ending the quote here, so break out
							doubleQuoted = False
							value = str[startIndex+1..a]
							a += 1
							Exit
						End

					' should we be ending the attribute?
					ElseIf a = endIndex-1 Or (Not singleQuoted And Not doubleQuoted And (str[a]=ASC_SPACE Or str[a]=ASC_TAB Or str[a]=ASC_LF Or str[a]=ASC_CR))
						If a=endIndex-1 Then
							value = str[startIndex..endIndex]
						Else
							value = str[startIndex..a]
						End
						a += 1
						Exit
					End
					a += 1
				End
				startIndex = a
				value = UnescapeXMLString(value)

				If singleQuoted Or doubleQuoted Then AssertError("Unclosed quote detected.")
			End

			' set the attribute
			e.SetAttribute(key, value)

			If a >= endIndex Then Exit
		End
		Return e
	End

	Method ParseFile:XMLDocument(filename:String)
		Return ParseString(LoadString(filename))
	End

	' parses an xml doc, currently doesn't support nested PI or prolog
	Method ParseString:XMLDocument(str:String)
		Self.str = str

		Local doc:XMLDocument = New XMLDocument
		Local elements:ArrayList<XMLElement> = New ArrayList<XMLElement>
		Local thisE:XMLElement = Null, newE:XMLElement = Null
		Local index:Int = 0, a:Int, b:Int, c:Int, nextIndex:Int

		' find first opening tag
		a = str.Find("<", index)
		While a >= index
			' read text between tags
			If a > index And str[index..a].Trim() <> "" Then
				If thisE <> Null Then
					thisE.value += UnescapeXMLString(str[index..a].Trim())
				Else
					AssertError("Loose text outside of any tag!")
				End
			End
			' check for PI
			If str[a+1] = ASC_QUESTION Then
				' die if the PI is inside the document
				If thisE <> Null Then AssertError("Processing instruction detected inside main document tag.")
				' create PI element up until next unquoted ?> after a
				nextIndex = FindUnquoted("?>", a+2)
				newE = GetTagContents(a+2, nextIndex)
				newE.pi = True
				doc.pi.Add(newE)
				newE = Null
				nextIndex += 2
			' check for prolog
			ElseIf str[a+1] = ASC_EXCLAMATION Then
				' if the next two chars are -- it's a comment
				If str[a+2] = ASC_HYPHEN And str[a+3] = ASC_HYPHEN Then
					' ignore everything until the next -->
					nextIndex = str.Find("-->", a+4)
					' if we couldn't find a comment end, die
					If nextIndex < 0 Then AssertError("Unclosed comment detected.")
					' XML specifications say that ---> is invalid
					If str[nextIndex-1] = ASC_HYPHEN Then AssertError("Invalid comment close detected (too many hyphens).")
					nextIndex += 3

				' if the next seven chars are [CDATA[ it's a cdata block
				ElseIf str.Find("[CDATA[", a+2) = a+2 Then
					' die if the CDATA is outside the document
					If thisE = Null Then AssertError("CDATA detected outside main document tag.")
					nextIndex = str.Find("]]>", a+9)
					' die if it doesn't end
					If nextIndex < 0 Then AssertError("Unclosed CDATA tag detected.")
					newE = New XMLElement
					newE.value = str[a+9..nextIndex]
					newE.cdata = True
					thisE.AddChild(newE)
					newE = Null
					nextIndex += 3

				' if the next seven chars are DOCTYPE it's a dtd tag
				ElseIf str.Find("DOCTYPE", a+2) = a+2 Then
					' die if the doctype is inside the document
					If thisE <> Null Then AssertError("DOCTYPE detected inside main document tag.")
					nextIndex = FindUnquoted(">", a+9)
					newE = GetTagContents(a+9, nextIndex)
					newE.prolog = True
					doc.prologs.Add(newE)
					newE = Null
					nextIndex += 1

				' don't know!
				Else
					AssertError("Unknown prolog detected.")
				End

			' check for closing tag
			ElseIf str[a+1] = ASC_SLASH Then
				' if no current element, die
				If thisE = Null Then AssertError("Closing tag found outside main document tag.")
				' find the next >
				nextIndex = str.Find(">", a+2)
				' if not found, the closing tag is broken
				If nextIndex < 0 Then AssertError("Incomplete closing tag detected.")
				' check that the tag name matches
				If str[a+2..nextIndex].Trim() <> thisE.name Then AssertError("Closing tag ~q"+str[a+2..nextIndex].Trim()+"~q does not match opening tag ~q"+thisE.name+"~q")
				If Not elements.IsEmpty() Then
					thisE = elements.RemoveLast()
				Else
					doc.root = thisE
					Exit
				End
				nextIndex += 1

			' check for opening tag
			Else
				' look for the end of the tag, and whether it's self-closing
				b = FindUnquoted("/>", a+1)
				c = FindUnquoted(">", a+1)
				' if we couldn't find either
				If c < 0 Then
					AssertError("Incomplete opening tag detected.")

				' if it's not self-closing
				ElseIf b < 0 Or c < b Then
					' get the new one
					newE = GetTagContents(a+1, c)
					If thisE <> Null Then
						' push the current element to the stack
						elements.AddLast(thisE)
						' add it as a child element
						thisE.AddChild(newE)
					End
					thisE = newE
					newE = Null
					nextIndex = c+1
				' it's self-closing
				Else
					newE = GetTagContents(a+1, b)
					If thisE <> Null Then
						thisE.AddChild(newE)
					Else
						doc.root = newE
						Exit
					End
					newE = Null
					nextIndex = b+2
				End
			End
			index = nextIndex
			a = str.Find("<", index)
		End
		If doc.root = Null Then AssertError("Error parsing XML: no document tag found.")
		Return doc
	End
End

Class XMLDocument
Private
	Field xmlVersion:String = "1.0"
	Field xmlEncoding:String = "UTF-8"
	Field root:XMLElement
	Field pi:ArrayList<XMLElement> = New ArrayList<XMLElement>
	Field prologs:ArrayList<XMLElement> = New ArrayList<XMLElement>

Public

' Constructors
	Method New(rootName:String="")
		If rootName <> "" Then root = New XMLElement(rootName)
	End

	Method New(root:XMLElement)
		Self.root = root
	End

' Methods
	Method ExportString:String(formatXML:Bool = True)
		' we start with the xml instruction
		Local output:String = "<?xml version=~q"+xmlVersion+"~q encoding=~q"+xmlEncoding+"~q?>"
		If formatXML Then output += "~n"
		' root node
		output += root.ToString(formatXML) + "~n"
		' done!
		Return output
	End

	Method SaveFile:Void(filename:String)
		' TODO when file IO is ready!
	End

' Properties
	Method Root:XMLElement() Property
		Return root
	End

	Method Prologs:ArrayList<XMLElement>() Property
		Return prologs
	End

	Method ProcessingInstructions:ArrayList<XMLElement>() Property
		Return pi
	End

	Method Version:String() Property
		Return xmlVersion
	End

	Method Encoding:String() Property
		Return xmlEncoding
	End
End

Class XMLAttribute
Private
	Field name:String
	Field value:String

' Constructors
	Method New(name:String, value:String)
		Self.name = name
		Self.value = value
	End
End

Class XMLElement
Private
	Field openTagStart:String = "<"
	Field openTagEnd:String = ">"
	Field selfCloseEnd:String = " />"
	Field closeTagStart:String = "</"
	Field closeTagEnd:String = ">"

	Field name:String
	Field attributes:ArrayList<XMLAttribute> = New ArrayList<XMLAttribute>
	Field children:ArrayList<XMLElement> = New ArrayList<XMLElement>
	Field value:String
	Field parent:XMLElement

	Field pi:Bool
	Field prolog:Bool
	Field cdata:Bool

Public
' Constructors
	Method New()
	End

	Method New(name:String, parent:XMLElement = Null)
		Self.parent = parent
		Self.name = name
		If parent <> Null Then parent.children.Add(Self)
	End

' Methods
	Method IsProcessingInstruction:Bool()
		Return pi
	End

	Method IsProlog:Bool()
		Return prolog
	End

	Method IsCharacterData:Bool()
		Return cdata
	End

	' avoid using this method if you can, because you should try not to have "floating" elements
	Method AddChild:Void(child:XMLElement)
		If children.Contains(child) Return
		children.Add(child)
		child.parent = Self
	End

	Method HasAttribute:Bool(name:String)
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then Return True
		Next
		Return False
	End

	Method GetAttribute:String(name:String, defaultValue:String = "")
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then Return att.value
		Next
		Return defaultValue
	End

	Method SetAttribute:String(name:String, value:String)
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then
				Local old:String = att.value
				att.value = value
				Return old
			End
		Next
		attributes.Add(New XMLAttribute(name, value))
		Return ""
	End

	Method ClearAttribute:String(name:String)
		For Local i% = 0 Until attributes.Size
			Local att:XMLAttribute = attributes.Get(i)
			If att.name = name Then
				attributes.Remove(att)
				Return att.value
			End
		Next
		Return ""
	End

	Method Dispose:Void(removeSelf:Bool = True)
		' dispose children
		Local en:IEnumerator<XMLElement> = children.Enumerator()
		While en.HasNext()
			Local element:XMLElement = en.NextObject()
			element.Dispose(False)
			en.Remove()
		End
		' remove self from parent if this is not recursing
		If removeSelf And parent <> Null Then parent.children.Remove(Self)
		' clear out the parent
		parent = Null
	End

	Method ToString:String(formatXML:Bool=True, indentation:Int=0)
		Local rv:String = ""
		If formatXML Then
			For Local i% = 0 Until indentation
				rv += "  "
			Next
		End
		rv += openTagStart + name
		If Not attributes.IsEmpty() Then
			For Local i% = 0 Until attributes.Size
				Local att:XMLAttribute = attributes.Get(i)
				rv += " " + att.name + "=~q" + EscapeXMLString(att.value) + "~q"
			Next
		End
		If children.IsEmpty() Then
			Local esc:String = EscapeXMLString(value.Trim())
			If esc.Length() = 0
				rv += selfCloseEnd
			Else
				rv += openTagEnd + value.Trim() + closeTagStart + name + closeTagEnd
			End
		Else
			rv += openTagEnd
			If formatXML Then
				rv += "~n"
			End
			For Local i% = 0 Until children.Size
				rv += children.Get(i).ToString(formatXML, indentation + 1)
			End
			Local esc:String = EscapeXMLString(value.Trim())
			If esc.Length() > 0 Then
				If Not formatXML Then
					rv += esc
				Else
					rv += "~n" + esc + "~n"
				End
			End
			If formatXML Then
				For Local i% = 0 Until indentation
					rv += "  "
				Next
			End
			rv += closeTagStart + name + closeTagEnd
		End
		If formatXML Then
			rv += "~n"
		End
		Return rv
	End

	Method GetChildrenByName:ArrayList<XMLElement>(findName:String)
		Local rv:ArrayList<XMLElement> = New ArrayList<XMLElement>
		For Local element:XMLElement = EachIn children
			If element.name = findName Then rv.Add(element)
		Next
		Return rv
	End

' Properties
	Method Children:ArrayList<XMLElement>() Property
		Return children
	End

	Method Parent:XMLElement() Property
		Return parent
	End

	Method Name:String() Property
		Return name
	End

	Method Value:String() Property
		Return value
	End
End

Function EscapeXMLString:String(str:String)
	str = str.Replace("&", "&")
	str = str.Replace("<", "<")
	str = str.Replace(">", ">")
	str = str.Replace("'", "'")
	str = str.Replace("~q", """)
	Return str
End

Function UnescapeXMLString:String(str:String)
	str = str.Replace(""", "~q")
	str = str.Replace("'", "'")
	str = str.Replace(">", ">")
	str = str.Replace("<", "<")
	str = str.Replace("&", "&")
	Return str
End


Remove debug print statement in Text

Remove debug print statement in Text

Strict

Private

Import baseobject
Import director
Import mojo
Import vector2d
Import vendor.angelfont

Public

Class Text Extends BaseObject
    Private

    Field _align:Int = ALIGN_LEFT
    Field _text:String = ""
    Field name:String
    Global angelFontStore:StringMap<AngelFont> = New StringMap<AngelFont>()

    Public

    Const ALIGN_LEFT:Int = AngelFont.ALIGN_LEFT
    Const ALIGN_CENTER:Int = AngelFont.ALIGN_CENTER
    Const ALIGN_RIGHT:Int = AngelFont.ALIGN_RIGHT

    Method New(name:String, pos:Vector2D=Null)
        If pos = Null Then pos = New Vector2D(0, 0)

        Self.name = name
        Self.pos = pos
    End

    Method OnCreate:Void(director:Director)
        Super.OnCreate(director)

        If Not angelFontStore.Contains(name)
            angelFontStore.Set(name, New AngelFont())
            angelFontStore.Get(name).LoadFont(name)
        End

        text = _text
    End

    Method OnRender:Void()
        If color Then color.Activate()
        angelFont.DrawText(_text, pos.x, pos.y, _align)
        If color Then color.Deactivate()
    End

    Method TextWidth:Int(char:String)
        Return angelFont.TextWidth(char)
    End

    Method TextHeight:Int(char:String)
        Return angelFont.TextHeight(char)
    End

    Method text:Void(newText:String) Property
        _text = newText
        If Not angelFont Then Return

        Local width:Float = angelFont.TextWidth(newText)
        Local height:Float = angelFont.TextHeight(newText)
        size = New Vector2D(width, height)
    End

    Method text:String() Property
        Return _text
    End

    Method align:Void(newAlign:Int) Property
        Select newAlign
        Case ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT
            _align = newAlign
        Default
            Error("Invalid align value specified.")
        End
    End

    Method align:Int() Property
        Return _align
    End

    Private

    Method angelFont:AngelFont() Property
        Return angelFontStore.Get(name)
    End
End

Strict

Private

Import baseobject
Import director
Import mojo
Import vector2d
Import vendor.angelfont

Public

Class Text Extends BaseObject
    Private

    Field _align:Int = ALIGN_LEFT
    Field _text:String = ""
    Field name:String
    Global angelFontStore:StringMap<AngelFont> = New StringMap<AngelFont>()

    Public

    Const ALIGN_LEFT:Int = AngelFont.ALIGN_LEFT
    Const ALIGN_CENTER:Int = AngelFont.ALIGN_CENTER
    Const ALIGN_RIGHT:Int = AngelFont.ALIGN_RIGHT

    Method New(name:String, pos:Vector2D=Null)
        If pos = Null Then pos = New Vector2D(0, 0)

        Self.name = name
        Self.pos = pos
    End

    Method OnCreate:Void(director:Director)
        Super.OnCreate(director)

        If Not angelFontStore.Contains(name)
            Print "LOADED"
            angelFontStore.Set(name, New AngelFont())
            angelFontStore.Get(name).LoadFont(name)
        End

        text = _text
    End

    Method OnRender:Void()
        If color Then color.Activate()
        angelFont.DrawText(_text, pos.x, pos.y, _align)
        If color Then color.Deactivate()
    End

    Method TextWidth:Int(char:String)
        Return angelFont.TextWidth(char)
    End

    Method TextHeight:Int(char:String)
        Return angelFont.TextHeight(char)
    End

    Method text:Void(newText:String) Property
        _text = newText
        If Not angelFont Then Return

        Local width:Float = angelFont.TextWidth(newText)
        Local height:Float = angelFont.TextHeight(newText)
        size = New Vector2D(width, height)
    End

    Method text:String() Property
        Return _text
    End

    Method align:Void(newAlign:Int) Property
        Select newAlign
        Case ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT
            _align = newAlign
        Default
            Error("Invalid align value specified.")
        End
    End

    Method align:Int() Property
        Return _align
    End

    Private

    Method angelFont:AngelFont() Property
        Return angelFontStore.Get(name)
    End
End


Some fixes

Some fixes

#rem
	header:This module contains the FlxBasic class and FlxClass interface.
#end
Strict

Import flxextern
Import flxcamera
Import flxg

#Rem
summary:This is a useful "generic" Flixel object.
Both [a flxobject.monkey.html]FlxObject[/a] and [a flxgroup.monkey.html]FlxGroup[/a] extend this class,
as do the plugins.  Has no size, position or graphical data.
#End
Class FlxBasic

	Global _ACTIVECOUNT:Int

	Global _VISIBLECOUNT:Int

	Global _CLASS:FlxClass = New FlxBasicClass()

	Global EXISTS_COMPARATOR:FlxBasicComparator = new FlxBasicExistsComparator()

	Global ACTIVE_COMPARATOR:FlxBasicComparator = new FlxBasicActiveComparator()

	Global VISIBLE_COMPARATOR:FlxBasicComparator = new FlxBasicVisibleComparator()

	Global ALIVE_COMPARATOR:FlxBasicComparator = new FlxBasicAliveComparator()

	#Rem
	summary:IDs seem like they could be pretty useful, huh?
	They're not actually used for anything yet though.
	#End
	Field ID:Int

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] and [a #Draw]Draw()[/a] are automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field exists:Bool

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] is automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field active:Bool

	#Rem
	summary:See details.
	Controls whether [a #Draw]Draw()[/a] is automatically called by[a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field visible:Bool

	#Rem
	summary:Useful state for many game objects - "dead" (!alive) vs alive.
	[a #Kill]Kill()[/a] and [a #Recive]Revive()[/a] both flip this switch (along with exists, but you can override that).
	#End
	Field alive:Bool

	#Rem
	summary:Setting this to true will prevent the object from appearing when the visual debug mode in the debugger overlay is toggled on.
	#End
	Field ignoreDrawDebug:Bool

	#Rem
	summary:Instantiate the basic flixel object.
	#End
	Method New()
		ID = -1
		exists = True
		active = True
		visible = True
		alive = True
		ignoreDrawDebug = False
	End Method

	#Rem
	summary:See details.
	Override this function to null out variables or manually call [a #Destroy]Destroy()[/a] on class members if necessary.
	Don't forget to call [b]super.Destroy()[/b]!
	#End
	Method Destroy:Void()
	End Method

	#Rem
	summary:See details.
	Pre-update is called right before [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PreUpdate:Void()
		_ACTIVECOUNT+=1
	End Method

	#Rem
	summary:Override this function to update your class's position and appearance.
	This is where most of your game rules and behavioral code will go.
	#End
	Method Update:Void()
	End Method

	#Rem
	summary:See details.
	Post-update is called right after [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PostUpdate:Void()
	End Method

	#Rem
	summary:Override this function to control how the object is drawn.
	Overriding [a #Draw]Draw()[/a] is rarely necessary, but can be very useful.
	#End
	Method Draw:Void()
		_VISIBLECOUNT+=1
	End Method

	#Rem
	summary:Override this function to draw custom "debug mode" graphics to the specified camera while the debugger's visual mode is toggled on.
	Params:
	[list]
	[*][a flxcamera.monkey.html]camera:FlxCamera[/a] - which camera to draw the debug visuals to.
	[/list]
	#End
	Method DrawDebug:Void(camera:FlxCamera = Null)
	End Method

	#Rem
	summary: Handy function for "killing" game objects.
	Default behavior is to flag them as nonexistent AND dead.
	However, if you want the "corpse" to remain in the game,
	like to animate an effect or whatever, you should override this,
	setting only alive to false, and leaving exists true.
	#End
	Method Kill:Void()
		alive = False
		exists = False
	End Method

	#Rem
	summary:Handy function for bringing game objects "back to life". Just sets alive and exists back to true.
	In practice, this function is most often called by [a flxobject.monkey.html#Reset]FlxObject.Reset()[/a].
	#End
	Method Revive:Void()
		alive = True
		exists = True
	End Method

	#Rem
	summary:Convert object to readable string name.  Useful for debugging, save games, etc.
	#End
	Method ToString:String()
		Return "FlxBasic"
	End Method

End Class

Interface FlxBasicInvoker

	Method Invoke:Void(object:FlxBasic)

End Interface

Interface FlxBasicSetter

	Method Set:Void(object:FlxBasic, value:Object)

End Interface

Interface FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic,rhs:FlxBasic)

End Interface

Private
Class FlxBasicClass Implements FlxClass

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

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

End Class

Class FlxBasicExistsComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.exists) Then
			If (rhs.exists) Return 0
			Return 1
		Else
			If (Not rhs.exists) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicActiveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.active) Then
			If (rhs.active) Return 0
			Return 1
		Else
			If (Not rhs.active) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicVisibleComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.visible) Then
			If (rhs.visible) Return 0
			Return 1
		Else
			If (Not rhs.visible) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicAliveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.alive) Then
			If (rhs.alive) Return 0
			Return 1
		Else
			If (Not rhs.alive) Return 0
			Return -1
		End If
	End Method

End Class

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

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

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

#rem
	header:This module contains the FlxBasic class and FlxClass interface.
#end
Strict

Import flxextern
Import flxcamera
Import flxg

#Rem
summary:This is a useful "generic" Flixel object.
Both [a flxobject.monkey.html]FlxObject[/a] and [a flxgroup.monkey.html]FlxGroup[/a] extend this class,
as do the plugins.  Has no size, position or graphical data.
#End
Class FlxBasic Implements FlxStringable

	Global _ACTIVECOUNT:Int

	Global _VISIBLECOUNT:Int

	Global _CLASS:FlxClass = New FlxBasicClass()

	Global EXISTS_COMPARATOR:FlxBasicComparator = new FlxBasicExistsComparator()

	Global ACTIVE_COMPARATOR:FlxBasicComparator = new FlxBasicActiveComparator()

	Global VISIBLE_COMPARATOR:FlxBasicComparator = new FlxBasicVisibleComparator()

	Global ALIVE_COMPARATOR:FlxBasicComparator = new FlxBasicAliveComparator()

	#Rem
	summary:IDs seem like they could be pretty useful, huh?
	They're not actually used for anything yet though.
	#End
	Field ID:Int

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] and [a #Draw]Draw()[/a] are automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field exists:Bool

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] is automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field active:Bool

	#Rem
	summary:See details.
	Controls whether [a #Draw]Draw()[/a] is automatically called by[a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field visible:Bool

	#Rem
	summary:Useful state for many game objects - "dead" (!alive) vs alive.
	[a #Kill]Kill()[/a] and [a #Recive]Revive()[/a] both flip this switch (along with exists, but you can override that).
	#End
	Field alive:Bool

	#Rem
	summary:Setting this to true will prevent the object from appearing when the visual debug mode in the debugger overlay is toggled on.
	#End
	Field ignoreDrawDebug:Bool

	#Rem
	summary:Instantiate the basic flixel object.
	#End
	Method New()
		ID = -1
		exists = True
		active = True
		visible = True
		alive = True
		ignoreDrawDebug = False
	End Method

	#Rem
	summary:See details.
	Override this function to null out variables or manually call [a #Destroy]Destroy()[/a] on class members if necessary.
	Don't forget to call [b]super.Destroy()[/b]!
	#End
	Method Destroy:Void()
	End Method

	#Rem
	summary:See details.
	Pre-update is called right before [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PreUpdate:Void()
		_ACTIVECOUNT+=1
	End Method

	#Rem
	summary:Override this function to update your class's position and appearance.
	This is where most of your game rules and behavioral code will go.
	#End
	Method Update:Void()
	End Method

	#Rem
	summary:See details.
	Post-update is called right after [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PostUpdate:Void()
	End Method

	#Rem
	summary:Override this function to control how the object is drawn.
	Overriding [a #Draw]Draw()[/a] is rarely necessary, but can be very useful.
	#End
	Method Draw:Void()
		_VISIBLECOUNT+=1
	End Method

	#Rem
	summary:Override this function to draw custom "debug mode" graphics to the specified camera while the debugger's visual mode is toggled on.
	Params:
	[list]
	[*][a flxcamera.monkey.html]camera:FlxCamera[/a] - which camera to draw the debug visuals to.
	[/list]
	#End
	Method DrawDebug:Void(camera:FlxCamera = Null)
	End Method

	#Rem
	summary: Handy function for "killing" game objects.
	Default behavior is to flag them as nonexistent AND dead.
	However, if you want the "corpse" to remain in the game,
	like to animate an effect or whatever, you should override this,
	setting only alive to false, and leaving exists true.
	#End
	Method Kill:Void()
		alive = False
		exists = False
	End Method

	#Rem
	summary:Handy function for bringing game objects "back to life". Just sets alive and exists back to true.
	In practice, this function is most often called by [a flxobject.monkey.html#Reset]FlxObject.Reset()[/a].
	#End
	Method Revive:Void()
		alive = True
		exists = True
	End Method

	#Rem
	summary:Convert object to readable string name.  Useful for debugging, save games, etc.
	#End
	Method ToString:String()
		Return "FlxBasic"
	End Method

End Class

Interface FlxBasicInvoker

	Method Invoke:Void(object:FlxBasic)

End Interface

Interface FlxBasicSetter

	Method Set:Void(object:FlxBasic, value:Object)

End Interface

Interface FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic,rhs:FlxBasic)

End Interface

Private
Class FlxBasicClass Implements FlxClass

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

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

End Class

Class FlxBasicExistsComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.exists) Then
			If (rhs.exists) Return 0
			Return 1
		Else
			If (Not rhs.exists) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicActiveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.active) Then
			If (rhs.active) Return 0
			Return 1
		Else
			If (Not rhs.active) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicVisibleComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.visible) Then
			If (rhs.visible) Return 0
			Return 1
		Else
			If (Not rhs.visible) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicAliveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.alive) Then
			If (rhs.alive) Return 0
			Return 1
		Else
			If (Not rhs.alive) Return 0
			Return -1
		End If
	End Method

End Class

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

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

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