FlxObject port complete

FlxObject port complete

Strict

#rem
	header:This module contains the FlxObject class.
#end
Import flxbasic
Import flxpoint
Import flxpath
Import flxgroup
Import flxcamera
Import flxtilemap
Import flxg
Import flxu

Import plugin.monkey.flxcolor

#Rem
summary:This is the base class for most of the display objects (FlxSprite, FlxText, etc).
It includes some basic attributes about game objects, including retro-style flickering,
basic state information, sizes, scrolling, and basic physics and motion.
#End
Class FlxObject Extends FlxBasic

	Global CLASS_OBJECT:FlxClass = new FlxObjectClass()

	Global X_COMPARATOR:FlxBasicComparator = new FlxObjectXComparator()

	Global Y_COMPARATOR:FlxBasicComparator = new FlxObjectYComparator()

	'summary:Generic value for "left" Used by facing, allowCollisions, and touching.
	Const LEFT:Int = 1

	'summary:Generic value for "right" Used by facing, allowCollisions, and touching.
	Const RIGHT:Int = 2

	'summary:Generic value for "up" Used by facing, allowCollisions, and touching.
	Const UP:Int = 4

	'summary:Generic value for "down" Used by facing, allowCollisions, and touching.
	Const DOWN:Int = 8

	'summary:Special-case constant meaning no collisions, used mainly by allowCollisions and touching.
	Const NONE:Int = 0

	'summary:Special-case constant meaning up, used mainly by allowCollisions and touching.
	Const CEILING:Int = UP

	'summary:Special-case constant meaning down, used mainly by allowCollisions and touching.
	Const FLOOR:Int = DOWN

	'summary:Special-case constant meaning only the left and right sides, used mainly by allowCollisions and touching.
	Const WALL:Int = LEFT | RIGHT

	'summary:Special-case constant meaning any direction, used mainly by allowCollisions and touching.
	Const ANY:Int = LEFT | RIGHT | UP | DOWN

	'summary:Handy constant used during collision resolution (see separateX() and separateY()).
	Const OVERLAP_BIAS:Int = 4

	'summary:Path behavior controls: move from the start of the path to the end then stop.
	Const PATH_FORWARD:Int = 0

	'summary:Path behavior controls: move from the end of the path to the start then stop.
	Const PATH_BACKWARD:Int = 1

	'summary:Path behavior controls: move from the start of the path to the end then directly back to the start, and start over.
	Const PATH_LOOP_FORWARD:Int = 2

	'summary:Path behavior controls: move from the end of the path to the start then directly back to the end, and start over.
	Const PATH_LOOP_BACKWARD:Int = 4

	'summary:Path behavior controls: move from the start of the path to the end then turn around and go back to the start, over and over.
	Const PATH_YOYO:Int = 8

	'summary:Path behavior controls: ignores any vertical component to the path data, only follows side to side.
	Const PATH_HORIZONTAL_ONLY:Int = 16

	'summary:Path behavior controls: ignores any horizontal component to the path data, only follows up and down.
	Const PATH_VERTICAL_ONLY:Int = 32

	'summary:X position of the upper left corner of this object in world space.
	Field x:Float

	'summary:Y position of the upper left corner of this object in world space.
	Field y:Float

	'summary:The width of this object.
	Field width:Float

	'summary:The height of this object.
	Field height:Float

	'summary:Whether an object will move/alter position after a collision.
	Field immovable:Bool

	Field velocity:FlxPoint

	Field mass:Float

	Field elasticity:Float

	Field acceleration:FlxPoint

	Field drag:FlxPoint

	Field maxVelocity:FlxPoint

	Field angle:Float

	Field angularVelocity:Float

	Field angularAcceleration:Float

	Field angularDrag:Float

	Field maxAngular:Float

	Field scrollFactor:FlxPoint

	Field health:Float

	Field moves:Bool

	Field touching:Int

	Field wasTouching:Int

	Field allowCollisions:Int

	Field last:FlxPoint

	Field path:FlxPath

	Field pathSpeed:Float

	Field pathAngle:Float

Private
	Global _pZero:FlxPoint = New FlxPoint()

	Field _flicker:Bool

	Field _flickerTimer:Float

	Field _point:FlxPoint

	Field _rect:FlxRect

	Field _pathNodeIndex:Int

	Field _pathMode:Int

	Field _pathInc:Int

	Field _pathRotate:Bool

	Field _debugBoundingBoxColor:FlxColor

Public
	Method New(x:Float, y:Float, width:Float = 0, height:Float = 0)
		Self.x = x
		Self.y = y
		last = New FlxPoint(x, y)
		Self.width = width
		Self.height = height
		mass = 1.0
		elasticity = 0.0

		health = 1

		immovable = False
		moves = True

		touching = NONE
		wasTouching = NONE
		allowCollisions = ANY

		velocity = New FlxPoint()
		acceleration = New FlxPoint()
		drag = New FlxPoint()
		maxVelocity = New FlxPoint(10000, 10000)

		angle = 0
		angularVelocity = 0
		angularAcceleration = 0
		angularDrag = 0
		maxAngular = 10000

		scrollFactor = New FlxPoint(1.0, 1.0)
		_flicker = False
		_flickerTimer = 0

		_point = New FlxPoint()
		_rect = New	FlxRect()

		path = Null
		pathSpeed = 0
		pathAngle = 0
	End Method

	Method Destroy:Void()
		velocity = Null
		acceleration = Null
		drag = Null
		maxVelocity = Null
		scrollFactor = Null
		_point = Null
		_rect = Null
		last = Null
		cameras = Null
		If (path <> Null) path.Destroy()
		path = Null
		_debugBoundingBoxColor = Null
	End Method

	Method PreUpdate:Void()
		_ACTIVECOUNT += 1

		If (_flickerTimer <> 0) Then
			If (_flickerTimer > 0) Then
				_flickerTimer -= FlxG.elapsed
				If (_flickerTimer <= 0) Then
					_flickerTimer = 0
					_flicker = False
				End If
			End If
		End If

		last.x = x
		last.y = y

		If (path <> Null And pathSpeed <> 0 And path.nodes.Get(_pathNodeIndex) <> Null) Then
			_UpdatePathMotion()
		End If
	End Method

	Method PostUpdate:Void()
		If (moves) _UpdateMotion()

		wasTouching = touching
		touching = NONE
	End Method

	Method Draw:Void()
		If (Not OnScreen(FlxG._currentCamera)) Return

		_VISIBLECOUNT += 1
		If (FlxG.visualDebug And Not ignoreDrawDebug) DrawDebug(FlxG._currentCamera)
	End Method

	Method DrawDebug:Void(camera:FlxCamera = Null)
		Local boundingBoxX:Float = x - Int(camera.scroll.x * scrollFactor.x)
		Local boundingBoxY:Float = y - Int(camera.scroll.y * scrollFactor.y)

		If (boundingBoxX > 0) Then
			boundingBoxX += 0.0000001
		Else
			boundingBoxX -= 0.0000001
		End If

		If (boundingBoxY > 0) Then
			boundingBoxY += 0.0000001
		Else
			boundingBoxY -= 0.0000001
		End If

		Local boundingBoxWidth:Int
		Local boundingBoxHeight:Int

		If (width <> Int(width)) Then
			boundingBoxWidth = width
		Else
			boundingBoxWidth = width - 1
		End If

		If (height <> Int(height)) Then
			boundingBoxHeight = height
		Else
			boundingBoxHeight = height - 1
		End If

		If (_debugBoundingBoxColor = Null) _debugBoundingBoxColor = New FlxColor()
		If (allowCollisions) Then
			If (allowCollisions <> ANY) Then
				_debugBoundingBoxColor.SetARGB(FlxG.PINK)
			ElseIf (immovable)
				_debugBoundingBoxColor.SetARGB(FlxG.GREEN)
			Else
				_debugBoundingBoxColor.SetARGB(FlxG.RED)
			End if
		Else
			_debugBoundingBoxColor.SetARGB(FlxG.BLUE)
		End If

		SetAlpha(.5)
		SetColor(_debugBoundingBoxColor.r, _debugBoundingBoxColor.g, _debugBoundingBoxColor.b)

		DrawLine(boundingBoxX, boundingBoxY, boundingBoxX + boundingBoxWidth, boundingBoxY)
		DrawLine(boundingBoxX + boundingBoxWidth, boundingBoxY, boundingBoxX + boundingBoxWidth ,boundingBoxY + boundingBoxHeight)
		DrawLine(boundingBoxX + boundingBoxWidth ,boundingBoxY + boundingBoxHeight, boundingBoxX, boundingBoxY + boundingBoxHeight)
		DrawLine(boundingBoxX, boundingBoxY + boundingBoxHeight, boundingBoxX, boundingBoxY)

		FlxG._lastDrawingColor = _debugBoundingBoxColor.argb
		SetAlpha(1)
	End Method

	Method FollowPath:Void(path:FlxPath, speed:Float = 100, mode:Int = PATH_FORWARD, autoRotate:Bool = False)
		If (path.nodes.Length() <= 0) Then
			FlxG.Log("WARNING: Paths need at least one node in them to be followed.")
			Return
		End If

		Self.path = path
		pathSpeed = Abs(speed)
		_pathMode = mode
		_pathRotate = autoRotate

		If (_pathMode = PATH_BACKWARD Or _pathMode = PATH_LOOP_BACKWARD) Then
			_pathNodeIndex = Self.path.nodes.Length() - 1
			_pathInc = -1
		Else
			_pathNodeIndex = 0
			_pathInc = 1
		End If
	End Method

	Method StopFollowingPath:Void(destroyPath:Bool = False)
		pathSpeed = 0
		If (destroyPath And path <> Null) Then
			path.Destroy()
			path = Null
		End If
	End Method

	Method Overlaps:Bool(objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (objectOrGroup = Null) Return False

		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local i:Int = 0
			Local l:Int = members.Length()

			While(i < l)
				If (Overlaps(members[i], inScreenSpace, camera)) Then
					results = True
					Exit
				End if
				i += 1
			Wend

			Return results
		End If

		If (FlxTilemap(objectOrGroup) <> Null) Then
			Return FlxTilemap(objectOrGroup).overlaps(Self, inScreenSpace, camera)
		End If

		Local object:FlxObject = FlxObject(objectOrGroup)

		If (Not inScreenSpace) Then
			Return (object.x + object.width > x) And (object.x < x + width) And
				(object.y + object.height > y) And (object.y < y + height)
		End If

		If (camera = Null) camera = FlxG.camera
		Local objectScreenPos:FlxPoint = object.GetScreenXY(Null, camera)
		GetScreenXY(_point, camera)

		Return (objectScreenPos.x + object.width > _point.x) And (objectScreenPos.x < _point.x + width) And
			(objectScreenPos.y + object.height > _point.y) And (objectScreenPos.y < _point.y + height)
	End Method

	Method OverlapsAt:Bool(x:Float, y:Float, objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (objectOrGroup = Null) Return False

		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local i:Int = 0
			Local l:Int = members.Length()

			While(i < l)
				If (overlapsAt(x, y, members[i], inScreenSpace, camera)) Then
					results = True
					Exit
				End if
				i += 1
			Wend

			Return results
		End If

		If (FlxTilemap(objectOrGroup) <> Null) Then
			Local tilemap:FlxTilemap = FlxTilemap(objectOrGroup)
			Return tilemap.OverlapsAt(tilemap.x - (x - Self.x), tilemap.y - (y - Self.y), Self ,inScreenSpace, camera)
		End If

		Local object:FlxObject = FlxObject(objectOrGroup)

		If (Not inScreenSpace) Then
			Return (object.x + object.width > x) And (object.x < x + width) And
				(object.y + object.height > y) And (object.y < y + height)
		End If

		If (camera = Null) camera = FlxG.camera
		Local objectScreenPos:FlxPoint = object.GetScreenXY(Null, camera)

		point.x = x - Int(camera.scroll.x * scrollFactor.x)
		point.y = y - Int(camera.scroll.y * scrollFactor.y)

		If (point.x > 0) Then
			point.x += 0.0000001
		Else
			point.x -= 0.0000001
		End If

		If (point.y > 0) Then
			point.y += 0.0000001
		Else
			point.y -= 0.0000001
		End If

		Return (objectScreenPos.x + object.width > _point.x) And (objectScreenPos.x < _point.x + width) And
			(objectScreenPos.y + object.height > _point.y) And (objectScreenPos.y < _point.y + height)
	End Method

	Method OverlapsPoint:Bool(point:FlxPoint, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (Not inScreenSpace) Then
			Return (point.x > x) And (point.x < x + width) And (point.y > y) And (point.y < y + height)
		End If

		If (camera = Null) camera = FlxG.camera

		Local x:Float = point.x - camera.scroll.x
		Local y:Float = point.y - camera.scroll.y
		GetScreenXY(_point, camera)

		Return (x > _point.x) And (x < _point.x + width) And (y > _point.y) And (y < _point.y + height)
	End Method

	Method OnScreen:Bool(camera:FlxCamera = Null)
		If (camera = Null) camera = FlxG.camera

		GetScreenXY(_point, camera)
		Return (_point.x + width > 0) And (_point.x < camera.Width) And (_point.y + height > 0) And (_point.y < camera.Height)
	End Method

	Method GetScreenXY:FlxPoint(point:FlxPoint = Null, camera:FlxCamera = Null)
		If (point = Null) point = New FlxPoint()
		If (camera = Null) camera = FlxG.camera

		point.x = x - Int(camera.scroll.x * scrollFactor.x)
		point.y = y - Int(camera.scroll.y * scrollFactor.y)

		If (point.x > 0) Then
			point.x += 0.0000001
		Else
			point.x -= 0.0000001
		End If

		If (point.y > 0) Then
			point.y += 0.0000001
		Else
			point.y -= 0.0000001
		End If

		Return point
	End Method

	Method Flicker:Void(duration:Float = 1)
		_flickerTimer = duration
		If (_flickerTimer = 0) _flicker = False
	End Method

	Method Flickering:Bool() Property
		Return _flickerTimer <> 0
	End Method

	Method Solid:Bool() Property
		Return (allowCollisions & ANY) > NONE
	End Method

	Method Solid:Void(solid:Bool) Property
		If (solid) Then
			allowCollisions = ANY
		Else
			allowCollisions = NONE
		End If
	End Method

	Method GetMidpoint:FlxPoint(point:FlxPoint)
		If (point = Null) point = New FlxPoint()
		point.x = x + width * .5
		point.y = y + height * .5
		Return point
	End Method

	Method Reset:Void(x:Float, y:Float)
		Revive()
		touching = NONE
		wasTouching = NONE
		Self.x = x
		Self.y = y
		last.x = x
		last.y = y
		velocity.x = 0
		velocity.y = 0
	End Method

	Method IsTouching:Bool(direction:Int)
		Return (touching & direction) > NONE
	End Method

	Method JustTouched:Bool(direction:Int)
		Return (touching & direction) > NONE And (wasTouching & direction) <= NONE
	End Method

	Method Hurt:Void(damage:Float)
		health -= damage
		If (health <= 0) Kill()
	End Method

	Method Separate:Bool(object1:FlxObject, object2:FlxObject)
		Local separatedX:Bool = SeparateX(object1, object2)
		Local separatedY:Bool = SeparateY(object1, object2)

		Return separatedX Or separatedY
	End Method

	Method SeparateX:Bool(object1:FlxObject, object2:FlxObject)
		Local obj1immovable:Bool = object1.immovable
		Local obj2immovable:Bool = object2.immovable

		If (obj1immovable And obj2immovable) Return False

		If (FlxTilemap(object1) <> Null) Then
			_separateInvoker.methodToInvoke = FlxObjectSeparateInvoker.METHOD_SEAPARATE_X
			_separateInvoker.parent = Self
			Return FlxTilemap(object1).OverlapsWithCallback(object2, _separateInvoker)
		End If

		If (FlxTilemap(object2) <> Null) Then
			_separateInvoker.methodToInvoke = FlxObjectSeparateInvoker.METHOD_SEAPARATE_X
			_separateInvoker.parent = Self
			Return FlxTilemap(object2).OverlapsWithCallback(object1, _separateInvoker, True)
		End If

		Local overlap:Float = 0
		Local obj1delta:Float = object1.x - object1.last.x
		Local obj2delta:Float = object2.x - object2.last.x

		If (obj1delta <> obj2delta) Then
			Local obj1deltaAbs:Float = Abs(obj1delta)
			Local obj2deltaAbs:Float = Abs(obj2delta)
			Local obj1rect:FlxRect = New FlxRect(object1.x - Max(obj1delta, 0), object1.last.y, object1.width + obj1deltaAbs, object1.height)
			Local obj2rect:FlxRect = New FlxRect(object2.x - Max(obj2delta, 0), object2.last.y, object2.width + obj2deltaAbs, object2.height)

			If (obj1rect.x + obj1rect.width > obj2rect.x And obj1rect.x < obj2rect.x + obj2rect.width And obj1rect.y + obj1rect.height > obj2rect.y And obj1rect.y < obj2rect.y + obj2rect.height) Then
				Local maxOverlap:Float = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS

				If (obj1delta > obj2delta) Then
					overlap = object1.x + object1.width - object2.x

					If (overlap > maxOverlap Or Not (object1.allowCollisions & RIGHT) Or Not (object2.allowCollisions & LEFT)) Then
						overlap = 0
					Else
						object1.touching |= RIGHT
						object2.touching |= LEFT
					End If

				ElseIf (obj1delta < obj2delta) Then
					overlap = object1.x - object2.width - object2.x

					If (-overlap > maxOverlap Or Not (object1.allowCollisions & LEFT) Or Not (object2.allowCollisions & RIGHT)) Then
						overlap = 0
					Else
						object1.touching |= LEFT
						object2.touching |= RIGHT
					End If
				End If
			End If
		End If

		If (overlap <> 0) Then
			Local obj1v:Float = object1.velocity.x
			Local obj2v:Float = object2.velocity.x

			If (Not obj1immovable And Not obj2immovable) Then
				overlap *= .5
				object1.x -= overlap
				object2.x += overlap

				Local obj1velocity:Float = Sqrt((obj2v * obj2v * object2.mass) / object1.mass) * _Sgn(obj2v)
				Local obj2velocity:Float = Sqrt((obj1v * obj1v * object1.mass) / object2.mass) * _Sgn(obj1v)
				Local average:Float = (obj1velocity + obj2velocity) * .5

				obj1velocity -= average
				obj2velocity -= average
				object1.velocity.x = average + obj1velocity * object1.elasticity
				object2.velocity.x = average + obj2velocity * object2.elasticity

			ElseIf (Not obj1immovable) Then
				object1.x -= overlap
				object1.velocity.x = obj2v - obj1v * object1.elasticity

			ElseIf (Not obj2immovable) Then
				object2.x += overlap
				object2.velocity.x = obj1v - obj2v * object2.elasticity
			End If

			Return True
		Else
			Return False
		End If
	End Method

	Method SeparateY:Bool(object1:FlxObject, object2:FlxObject)
		Local obj1immovable:Bool = object1.immovable
		Local obj2immovable:Bool = object2.immovable

		If (obj1immovable And obj2immovable) Return False

		If (FlxTilemap(object1) <> Null) Then
			_separateInvoker.methodToInvoke = FlxObjectSeparateInvoker.METHOD_SEAPARATE_Y
			_separateInvoker.parent = Self
			Return FlxTilemap(object1).OverlapsWithCallback(object2, _separateInvoker)
		End If

		If (FlxTilemap(object2) <> Null) Then
			_separateInvoker.methodToInvoke = FlxObjectSeparateInvoker.METHOD_SEAPARATE_Y
			_separateInvoker.parent = Self
			Return FlxTilemap(object2).OverlapsWithCallback(object1, _separateInvoker, True)
		End If

		Local overlap:Float = 0
		Local obj1delta:Float = object1.y - object1.last.y
		Local obj2delta:Float = object2.y - object2.last.y

		If (obj1delta <> obj2delta) Then
			Local obj1deltaAbs:Float = Abs(obj1delta)
			Local obj2deltaAbs:Float = Abs(obj2delta)
			Local obj1rect:FlxRect = New FlxRect(object1.x, object1.y - Max(obj1delta, 0), object1.width, object1.height + obj1deltaAbs)
			Local obj2rect:FlxRect = New FlxRect(object2.x, object2.y - Max(obj2delta, 0), object2.width, object2.height +  + obj2deltaAbs)

			If (obj1rect.x + obj1rect.width > obj2rect.x And obj1rect.x < obj2rect.x + obj2rect.width And obj1rect.y + obj1rect.height > obj2rect.y And obj1rect.y < obj2rect.y + obj2rect.height) Then
				Local maxOverlap:Float = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS

				If (obj1delta > obj2delta) Then
					overlap = object1.y + object1.height - object2.y

					If (overlap > maxOverlap Or Not (object1.allowCollisions & DOWN) Or Not (object2.allowCollisions & UP)) Then
						overlap = 0
					Else
						object1.touching |= DOWN
						object2.touching |= UP
					End If

				ElseIf (obj1delta < obj2delta) Then
					overlap = object1.y - object2.height - object2.y

					If (-overlap > maxOverlap Or Not (object1.allowCollisions & UP) Or Not (object2.allowCollisions & DOWN)) Then
						overlap = 0
					Else
						object1.touching |= UP
						object2.touching |= DOWN
					End If
				End If
			End If
		End If

		If (overlap <> 0) Then
			Local obj1v:Float = object1.velocity.y
			Local obj2v:Float = object2.velocity.y

			If (Not obj1immovable And Not obj2immovable) Then
				overlap *= .5
				object1.y -= overlap
				object2.y += overlap

				Local obj1velocity:Float = Sqrt((obj2v * obj2v * object2.mass) / object1.mass) * _Sgn(obj2v)
				Local obj2velocity:Float = Sqrt((obj1v * obj1v * object1.mass) / object2.mass) * _Sgn(obj1v)
				Local average:Float = (obj1velocity + obj2velocity) * .5

				obj1velocity -= average
				obj2velocity -= average
				object1.velocity.y = average + obj1velocity * object1.elasticity
				object2.velocity.y = average + obj2velocity * object2.elasticity

			ElseIf (Not obj1immovable) Then
				object1.y -= overlap
				object1.velocity.y = obj2v - obj1v * object1.elasticity

				If (object2.active And object2.moves And obj1delta > obj2delta) Then
					object1.x += object2.x - object2.last.x
				End If

			ElseIf (Not obj2immovable) Then
				object2.y += overlap
				object2.velocity.y = obj1v - obj2v * object2.elasticity

				If (object1.active And object1.moves And obj1delta < obj2delta) Then
					object2.x += object1.x - object1.last.x
				End If
			End If

			Return True
		Else
			Return False
		End If
	End Method

	Method ToString:String()
		Return "FlxObject"
	End Method

Private
	Method _UpdateMotion:Void()
		Local delta:Float
		Local velocityDelta:Float

		velocityDelta = (FlxU.ComputeVelocity(angularVelocity, angularAcceleration, angularDrag, maxAngular) - angularVelocity) / 2
		angularVelocity += velocityDelta
		angle += angularVelocity * FlxG.elapsed
		angularVelocity += velocityDelta

		velocityDelta = (FlxU.ComputeVelocity(velocity.x, acceleration.x, drag.x, maxVelocity.x) - velocity.x) / 2
		velocity.x += velocityDelta
		delta = velocity.x * FlxG.elapsed
		velocity.x += velocityDelta
		x += delta

		velocityDelta = (FlxU.ComputeVelocity(velocity.y, acceleration.y, drag.y, maxVelocity.y) - velocity.y) / 2
		velocity.y += velocityDelta
		delta = velocity.y * FlxG.elapsed
		velocity.y += velocityDelta
		y += delta
	End Method

	Method _AdvancePath:FlxPoint(snap:Bool = True)
		If (snap) Then
			Local oldNode:FlxPoint = path.nodes.Get(_pathNodeIndex)
			If (oldNode <> Null) Then
				If ((_pathMode & PATH_VERTICAL_ONLY) = 0) Then
					x = oldNode.x - width * .5
				End If

				If ((_pathMode & PATH_HORIZONTAL_ONLY) = 0) Then
					y = oldNode.y - height * .5
				End If
			End If
		End If

		_pathNodeIndex += _pathInc

		If ((_pathMode & PATH_BACKWARD) > 0) Then
			If (_pathNodeIndex < 0) Then
				_pathNodeIndex = 0
				pathSpeed = 0
			End If

		ElseIf ((_pathMode & PATH_LOOP_FORWARD) > 0) Then
			If (_pathNodeIndex >= path.nodes.Length()) Then
				_pathNodeIndex = 0
			End If

		ElseIf ((_pathMode & PATH_LOOP_BACKWARD) > 0) Then
			If (_pathNodeIndex < 0) Then
				_pathNodeIndex = path.nodes.Length() - 1
				If (_pathNodeIndex < 0) _pathNodeIndex = 0
			End If

		ElseIf ((_pathMode & PATH_YOYO) > 0)
			If (_pathInc > 0) Then
				If (_pathNodeIndex >= path.nodes.Length()) Then
					_pathNodeIndex = path.nodes.Length() - 2

					If (_pathNodeIndex < 0) _pathNodeIndex = 0
					_pathInc = -_pathInc

				ElseIf (_pathNodeIndex < 0) Then
					_pathNodeIndex = 1
					If (_pathNodeIndex >= path.nodes.Length()) Then
						_pathNodeIndex = path.nodes.Length() -1
					End If

					If (_pathNodeIndex < 0) _pathNodeIndex = 0
					_pathInc = -_pathInc
				End If
			End If

		Else
			If (_pathNodeIndex >= path.nodes.Length()) Then
				_pathNodeIndex = path.nodes.Length() - 1
				pathSpeed = 0
			End If
		End If

		Return path.nodes.Get(_pathNodeIndex)
	End Method

	Method _UpdatePathMotion:Void()
		_point.x = x + width * .5
		_point.y = y + height * .5

		Local node:FlxPoint = path.nodes.Get(_pathNodeIndex)
		Local deltaX:Float = node.x - _point.x
		Local deltaY:Float = node.y - _point.y

		Local horizontalOnly:Bool = (_pathMode & PATH_HORIZONTAL_ONLY) > 0
		Local verticalOnly:Bool = (_pathMode & PATH_VERTICAL_ONLY) > 0

		If (horizontalOnly) Then
			If (Abs(deltaX) < pathSpeed * FlxG.elapsed) node = _AdvancePath()

		ElseIf (verticalOnly) Then
			If (Abs(deltaY) < pathSpeed * FlxG.elapsed) node = _AdvancePath()

		Else
			If (Sqrt(deltaX * deltaX + deltaY * deltaY) < pathSpeed * FlxG.elapsed) Then
				node = _AdvancePath()
			End If
		End If

		If (pathSpeed <> 0) Then
			_point.x = x + width * .5
			_point.y = y + height * .5
			If (horizontalOnly Or _point.y = node.y) Then
				If (_point.x < node.x) Then
					velocity.x = pathSpeed
				Else
					velocity.x = -pathSpeed
				End if

				If (velocity.x < 0) Then
					pathAngle = -90
				Else
					pathAngle = 90
				End If

				If (Not horizontalOnly) velocity.y = 0

			ElseIf (verticalOnly Or _point.x = node.x) Then
				If (_point.y < node.y) Then
					velocity.y = pathSpeed
				Else
					velocity.y = -pathSpeed
				End If

				If (velocity.y < 0) Then
					pathAngle = 0
				Else
					pathAngle = 180
				End If

				If (Not verticalOnly) velocity.x = 0

			Else
				pathAngle = FlxU.GetAngle(_point, node)
				FlxU.RotatePoint(0, pathSpeed, 0, 0, pathAngle, velocity)
			End If

			If (_pathRotate) Then
				angularVelocity = 0
				angularAcceleration = 0
				angle = pathAngle
			End If
		End If
	End Method

	Method _Sgn:Float(value:Float)
		If (value > 0) Return 1
		Return -1
	End Method

End Class

Private
Class FlxObjectClass Implements FlxClass

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

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

End Class

Class FlxObjectYComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		Return FlxObject(lhs).y - FlxObject(rhs).y
	End Method

End Class

Class FlxObjectXComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		Return FlxObject(lhs).x - FlxObject(rhs).x
	End Method

End Class

Class FlxObjectSeparateInvoker

	Const METHOD_SEAPARATE_X:Int = 0
	Const METHOD_SEPARATE_Y:Int = 1

	Field parent:FlxObject
	Field methodToInvoke:Int

	Method Invoke:Bool(object1:FlxObject, object2:FlxObject)
		If (methodToInvoke = METHOD_SEAPARATE_X) Then
			parent.SeparateX(object1, object2)
		Else
			parent.SeparateY(object1, object2)
		End If
	End Method

End Class

Private
	Global _separateInvoker:FlxObjectSeparateInvoker = New FlxObjectSeparateInvoker()

#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

Strict

#rem
	header:This module contains the FlxObject class.
#end
Import flxbasic
Import flxpoint
Import flxpath
Import flxgroup
Import flxcamera
Import flxtilemap
Import flxg
Import flxu

Import plugin.monkey.flxcolor

#Rem
summary:This is the base class for most of the display objects (FlxSprite, FlxText, etc).
It includes some basic attributes about game objects, including retro-style flickering,
basic state information, sizes, scrolling, and basic physics and motion.
#End
Class FlxObject Extends FlxBasic

	Global CLASS_OBJECT:FlxClass = new FlxObjectClass()

	Global X_COMPARATOR:FlxBasicComparator = new FlxObjectXComparator()

	Global Y_COMPARATOR:FlxBasicComparator = new FlxObjectYComparator()

	'summary:Generic value for "left" Used by facing, allowCollisions, and touching.
	Const LEFT:Int = 1

	'summary:Generic value for "right" Used by facing, allowCollisions, and touching.
	Const RIGHT:Int = 2

	'summary:Generic value for "up" Used by facing, allowCollisions, and touching.
	Const UP:Int = 4

	'summary:Generic value for "down" Used by facing, allowCollisions, and touching.
	Const DOWN:Int = 8

	'summary:Special-case constant meaning no collisions, used mainly by allowCollisions and touching.
	Const NONE:Int = 0

	'summary:Special-case constant meaning up, used mainly by allowCollisions and touching.
	Const CEILING:Int = UP

	'summary:Special-case constant meaning down, used mainly by allowCollisions and touching.
	Const FLOOR:Int = DOWN

	'summary:Special-case constant meaning only the left and right sides, used mainly by allowCollisions and touching.
	Const WALL:Int = LEFT | RIGHT

	'summary:Special-case constant meaning any direction, used mainly by allowCollisions and touching.
	Const ANY:Int = LEFT | RIGHT | UP | DOWN

	'summary:Handy constant used during collision resolution (see separateX() and separateY()).
	Const OVERLAP_BIAS:Int = 4

	'summary:Path behavior controls: move from the start of the path to the end then stop.
	Const PATH_FORWARD:Int = 0

	'summary:Path behavior controls: move from the end of the path to the start then stop.
	Const PATH_BACKWARD:Int = 1

	'summary:Path behavior controls: move from the start of the path to the end then directly back to the start, and start over.
	Const PATH_LOOP_FORWARD:Int = 2

	'summary:Path behavior controls: move from the end of the path to the start then directly back to the end, and start over.
	Const PATH_LOOP_BACKWARD:Int = 4

	'summary:Path behavior controls: move from the start of the path to the end then turn around and go back to the start, over and over.
	Const PATH_YOYO:Int = 8

	'summary:Path behavior controls: ignores any vertical component to the path data, only follows side to side.
	Const PATH_HORIZONTAL_ONLY:Int = 16

	'summary:Path behavior controls: ignores any horizontal component to the path data, only follows up and down.
	Const PATH_VERTICAL_ONLY:Int = 32

	'summary:X position of the upper left corner of this object in world space.
	Field x:Float

	'summary:Y position of the upper left corner of this object in world space.
	Field y:Float

	'summary:The width of this object.
	Field width:Float

	'summary:The height of this object.
	Field height:Float

	'summary:Whether an object will move/alter position after a collision.
	Field immovable:Bool

	Field velocity:FlxPoint

	Field mass:Float

	Field elasticity:Float

	Field acceleration:FlxPoint

	Field drag:FlxPoint

	Field maxVelocity:FlxPoint

	Field angle:Float

	Field angularVelocity:Float

	Field angularAcceleration:Float

	Field angularDrag:Float

	Field maxAngular:Float

	Field scrollFactor:FlxPoint

	Field health:Float

	Field moves:Bool

	Field touching:Int

	Field wasTouching:Int

	Field allowCollisions:Int

	Field last:FlxPoint

	Field path:FlxPath

	Field pathSpeed:Float

	Field pathAngle:Float

Private
	Global _pZero:FlxPoint = New FlxPoint()

	Field _flicker:Bool

	Field _flickerTimer:Float

	Field _point:FlxPoint

	Field _rect:FlxRect

	Field _pathNodeIndex:Int

	Field _pathMode:Int

	Field _pathInc:Int

	Field _pathRotate:Bool

	Field _debugBoundingBoxColor:FlxColor

Public
	Method New(x:Float, y:Float, width:Float = 0, height:Float = 0)
		Self.x = x
		Self.y = y
		last = New FlxPoint(x, y)
		Self.width = width
		Self.height = height
		mass = 1.0
		elasticity = 0.0

		health = 1

		immovable = False
		moves = True

		touching = NONE
		wasTouching = NONE
		allowCollisions = ANY

		velocity = New FlxPoint()
		acceleration = New FlxPoint()
		drag = New FlxPoint()
		maxVelocity = New FlxPoint(10000, 10000)

		angle = 0
		angularVelocity = 0
		angularAcceleration = 0
		angularDrag = 0
		maxAngular = 10000

		scrollFactor = New FlxPoint(1.0, 1.0)
		_flicker = False
		_flickerTimer = 0

		_point = New FlxPoint()
		_rect = New	FlxRect()

		path = Null
		pathSpeed = 0
		pathAngle = 0
	End Method

	Method Destroy:Void()
		velocity = Null
		acceleration = Null
		drag = Null
		maxVelocity = Null
		scrollFactor = Null
		_point = Null
		_rect = Null
		last = Null
		cameras = Null
		If (path <> Null) path.Destroy()
		path = Null
		_debugBoundingBoxColor = Null
	End Method

	Method PreUpdate:Void()
		_ACTIVECOUNT += 1

		If (_flickerTimer <> 0) Then
			If (_flickerTimer > 0) Then
				_flickerTimer -= FlxG.elapsed
				If (_flickerTimer <= 0) Then
					_flickerTimer = 0
					_flicker = False
				End If
			End If
		End If

		last.x = x
		last.y = y

		If (path <> Null And pathSpeed <> 0 And path.nodes.Get(_pathNodeIndex) <> Null) Then
			_UpdatePathMotion()
		End If
	End Method

	Method PostUpdate:Void()
		If (moves) _UpdateMotion()

		wasTouching = touching
		touching = NONE
	End Method

	Method Draw:Void()
		If (Not OnScreen(FlxG._currentCamera)) Return

		_VISIBLECOUNT += 1
		If (FlxG.visualDebug And Not ignoreDrawDebug) DrawDebug(FlxG._currentCamera)
	End Method

	Method DrawDebug:Void(camera:FlxCamera = Null)
		Local boundingBoxX:Float = x - Int(camera.scroll.x * scrollFactor.x)
		Local boundingBoxY:Float = y - Int(camera.scroll.y * scrollFactor.y)

		If (boundingBoxX > 0) Then
			boundingBoxX += 0.0000001
		Else
			boundingBoxX -= 0.0000001
		End If

		If (boundingBoxY > 0) Then
			boundingBoxY += 0.0000001
		Else
			boundingBoxY -= 0.0000001
		End If

		Local boundingBoxWidth:Int
		Local boundingBoxHeight:Int

		If (width <> Int(width)) Then
			boundingBoxWidth = width
		Else
			boundingBoxWidth = width - 1
		End If

		If (height <> Int(height)) Then
			boundingBoxHeight = height
		Else
			boundingBoxHeight = height - 1
		End If

		If (_debugBoundingBoxColor = Null) _debugBoundingBoxColor = New FlxColor()
		If (allowCollisions) Then
			If (allowCollisions <> ANY) Then
				_debugBoundingBoxColor.SetARGB(FlxG.PINK)
			ElseIf (immovable)
				_debugBoundingBoxColor.SetARGB(FlxG.GREEN)
			Else
				_debugBoundingBoxColor.SetARGB(FlxG.RED)
			End if
		Else
			_debugBoundingBoxColor.SetARGB(FlxG.BLUE)
		End If

		SetAlpha(.5)
		SetColor(_debugBoundingBoxColor.r, _debugBoundingBoxColor.g, _debugBoundingBoxColor.b)

		DrawLine(boundingBoxX, boundingBoxY, boundingBoxX + boundingBoxWidth, boundingBoxY)
		DrawLine(boundingBoxX + boundingBoxWidth, boundingBoxY, boundingBoxX + boundingBoxWidth ,boundingBoxY + boundingBoxHeight)
		DrawLine(boundingBoxX + boundingBoxWidth ,boundingBoxY + boundingBoxHeight, boundingBoxX, boundingBoxY + boundingBoxHeight)
		DrawLine(boundingBoxX, boundingBoxY + boundingBoxHeight, boundingBoxX, boundingBoxY)

		FlxG._lastDrawingColor = _debugBoundingBoxColor.argb
		SetAlpha(1)
	End Method

	Method FollowPath:Void(path:FlxPath, speed:Float = 100, mode:Int = PATH_FORWARD, autoRotate:Bool = False)
		If (path.nodes.Length() <= 0) Then
			FlxG.Log("WARNING: Paths need at least one node in them to be followed.")
			Return
		End If

		Self.path = path
		pathSpeed = Abs(speed)
		_pathMode = mode
		_pathRotate = autoRotate

		If (_pathMode = PATH_BACKWARD Or _pathMode = PATH_LOOP_BACKWARD) Then
			_pathNodeIndex = Self.path.nodes.Length() - 1
			_pathInc = -1
		Else
			_pathNodeIndex = 0
			_pathInc = 1
		End If
	End Method

	Method StopFollowingPath:Void(destroyPath:Bool = False)
		pathSpeed = 0
		If (destroyPath And path <> Null) Then
			path.Destroy()
			path = Null
		End If
	End Method

	Method Overlaps:Bool(objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (objectOrGroup = Null) Return False

		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local i:Int = 0
			Local l:Int = members.Length()

			While(i < l)
				If (Overlaps(members[i], inScreenSpace, camera)) Then
					results = True
					Exit
				End if
				i += 1
			Wend

			Return results
		End If

		If (FlxTilemap(objectOrGroup) <> Null) Then
			Return FlxTilemap(objectOrGroup).overlaps(Self, inScreenSpace, camera)
		End If

		Local object:FlxObject = FlxObject(objectOrGroup)

		If (Not inScreenSpace) Then
			Return (object.x + object.width > x) And (object.x < x + width) And
				(object.y + object.height > y) And (object.y < y + height)
		End If

		If (camera = Null) camera = FlxG.camera
		Local objectScreenPos:FlxPoint = object.GetScreenXY(Null, camera)
		GetScreenXY(_point, camera)

		Return (objectScreenPos.x + object.width > _point.x) And (objectScreenPos.x < _point.x + width) And
			(objectScreenPos.y + object.height > _point.y) And (objectScreenPos.y < _point.y + height)
	End Method

	Method OverlapsAt:Bool(x:Float, y:Float, objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (objectOrGroup = Null) Return False

		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local i:Int = 0
			Local l:Int = members.Length()

			While(i < l)
				If (overlapsAt(x, y, members[i], inScreenSpace, camera)) Then
					results = True
					Exit
				End if
				i += 1
			Wend

			Return results
		End If

		If (FlxTilemap(objectOrGroup) <> Null) Then
			Local tilemap:FlxTilemap = FlxTilemap(objectOrGroup)
			Return tilemap.OverlapsAt(tilemap.x - (x - Self.x), tilemap.y - (y - Self.y), Self ,inScreenSpace, camera)
		End If

		Local object:FlxObject = FlxObject(objectOrGroup)

		If (Not inScreenSpace) Then
			Return (object.x + object.width > x) And (object.x < x + width) And
				(object.y + object.height > y) And (object.y < y + height)
		End If

		If (camera = Null) camera = FlxG.camera
		Local objectScreenPos:FlxPoint = object.GetScreenXY(Null, camera)

		point.x = x - Int(camera.scroll.x * scrollFactor.x)
		point.y = y - Int(camera.scroll.y * scrollFactor.y)

		If (point.x > 0) Then
			point.x += 0.0000001
		Else
			point.x -= 0.0000001
		End If

		If (point.y > 0) Then
			point.y += 0.0000001
		Else
			point.y -= 0.0000001
		End If

		Return (objectScreenPos.x + object.width > _point.x) And (objectScreenPos.x < _point.x + width) And
			(objectScreenPos.y + object.height > _point.y) And (objectScreenPos.y < _point.y + height)
	End Method

	Method OverlapsPoint:Bool(point:FlxPoint, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (Not inScreenSpace) Then
			Return (point.x > x) And (point.x < x + width) And (point.y > y) And (point.y < y + height)
		End If

		If (camera = Null) camera = FlxG.camera

		Local x:Float = point.x - camera.scroll.x
		Local y:Float = point.y - camera.scroll.y
		GetScreenXY(_point, camera)

		Return (x > _point.x) And (x < _point.x + width) And (y > _point.y) And (y < _point.y + height)
	End Method

	Method OnScreen:Bool(camera:FlxCamera = Null)
		If (camera = Null) camera = FlxG.camera

		GetScreenXY(_point, camera)
		Return (_point.x + width > 0) And (_point.x < camera.Width) And (_point.y + height > 0) And (_point.y < camera.Height)
	End Method

	Method GetScreenXY:FlxPoint(point:FlxPoint = Null, camera:FlxCamera = Null)
		If (point = Null) point = New FlxPoint()
		If (camera = Null) camera = FlxG.camera

		point.x = x - Int(camera.scroll.x * scrollFactor.x)
		point.y = y - Int(camera.scroll.y * scrollFactor.y)

		If (point.x > 0) Then
			point.x += 0.0000001
		Else
			point.x -= 0.0000001
		End If

		If (point.y > 0) Then
			point.y += 0.0000001
		Else
			point.y -= 0.0000001
		End If

		Return point
	End Method

	Method Flicker:Void(duration:Float = 1)
		_flickerTimer = duration
		If (_flickerTimer = 0) _flicker = False
	End Method

	Method Flickering:Bool() Property
		Return _flickerTimer <> 0
	End Method

	Method Solid:Bool() Property
		Return (allowCollisions & ANY) > NONE
	End Method

	Method Solid:Void(solid:Bool) Property
		If (solid) Then
			allowCollisions = ANY
		Else
			allowCollisions = NONE
		End If
	End Method

	Method GetMidpoint:FlxPoint(point:FlxPoint)
		If (point = Null) point = New FlxPoint()
		point.x = x + width * .5
		point.y = y + height * .5
		Return point
	End Method

	Method Reset:Void(x:Float, y:Float)
		Revive()
		touching = NONE
		wasTouching = NONE
		Self.x = x
		Self.y = y
		last.x = x
		last.y = y
		velocity.x = 0
		velocity.y = 0
	End Method

	Method IsTouching:Bool(direction:Int)
		Return (touching & direction) > NONE
	End Method

	Method JustTouched:Bool(direction:Int)
		Return (touching & direction) > NONE And (wasTouching & direction) <= NONE
	End Method

	Method Hurt:Void(damage:Float)
		health -= damage
		If (health <= 0) Kill()
	End Method

	Method SeparateX:Bool(object1:FlxObject, object2:FlxObject)
		Local obj1immovable:Bool = object1.immovable
		Local obj2immovable:Bool = object2.immovable

		If (obj1immovable And obj2immovable) Return False

		'(FlxTilemap(object1) <> Null) Return FlxTilemap(object1).
		'TODO
	End Method

	Method SeparateY:Bool(object1:FlxObject, object2:FlxObject)

	End Method

	Method ToString:String()
		Return "FlxObject"
	End Method

Private
	Method _UpdateMotion:Void()
		Local delta:Float
		Local velocityDelta:Float

		velocityDelta = (FlxU.ComputeVelocity(angularVelocity, angularAcceleration, angularDrag, maxAngular) - angularVelocity) / 2
		angularVelocity += velocityDelta
		angle += angularVelocity * FlxG.elapsed
		angularVelocity += velocityDelta

		velocityDelta = (FlxU.ComputeVelocity(velocity.x, acceleration.x, drag.x, maxVelocity.x) - velocity.x) / 2
		velocity.x += velocityDelta
		delta = velocity.x * FlxG.elapsed
		velocity.x += velocityDelta
		x += delta

		velocityDelta = (FlxU.ComputeVelocity(velocity.y, acceleration.y, drag.y, maxVelocity.y) - velocity.y) / 2
		velocity.y += velocityDelta
		delta = velocity.y * FlxG.elapsed
		velocity.y += velocityDelta
		y += delta
	End Method

	Method _AdvancePath:FlxPoint(snap:Bool = True)
		If (snap) Then
			Local oldNode:FlxPoint = path.nodes.Get(_pathNodeIndex)
			If (oldNode <> Null) Then
				If ((_pathMode & PATH_VERTICAL_ONLY) = 0) Then
					x = oldNode.x - width * .5
				End If

				If ((_pathMode & PATH_HORIZONTAL_ONLY) = 0) Then
					y = oldNode.y - height * .5
				End If
			End If
		End If

		_pathNodeIndex += _pathInc

		If ((_pathMode & PATH_BACKWARD) > 0) Then
			If (_pathNodeIndex < 0) Then
				_pathNodeIndex = 0
				pathSpeed = 0
			End If

		ElseIf ((_pathMode & PATH_LOOP_FORWARD) > 0) Then
			If (_pathNodeIndex >= path.nodes.Length()) Then
				_pathNodeIndex = 0
			End If

		ElseIf ((_pathMode & PATH_LOOP_BACKWARD) > 0) Then
			If (_pathNodeIndex < 0) Then
				_pathNodeIndex = path.nodes.Length() - 1
				If (_pathNodeIndex < 0) _pathNodeIndex = 0
			End If

		ElseIf ((_pathMode & PATH_YOYO) > 0)
			If (_pathInc > 0) Then
				If (_pathNodeIndex >= path.nodes.Length()) Then
					_pathNodeIndex = path.nodes.Length() - 2

					If (_pathNodeIndex < 0) _pathNodeIndex = 0
					_pathInc = -_pathInc

				ElseIf (_pathNodeIndex < 0) Then
					_pathNodeIndex = 1
					If (_pathNodeIndex >= path.nodes.Length()) Then
						_pathNodeIndex = path.nodes.Length() -1
					End If

					If (_pathNodeIndex < 0) _pathNodeIndex = 0
					_pathInc = -_pathInc
				End If
			End If

		Else
			If (_pathNodeIndex >= path.nodes.Length()) Then
				_pathNodeIndex = path.nodes.Length() - 1
				pathSpeed = 0
			End If
		End If

		Return path.nodes.Get(_pathNodeIndex)
	End Method

	Method _UpdatePathMotion:Void()
		_point.x = x + width * .5
		_point.y = y + height * .5

		Local node:FlxPoint = path.nodes.Get(_pathNodeIndex)
		Local deltaX:Float = node.x - _point.x
		Local deltaY:Float = node.y - _point.y

		Local horizontalOnly:Bool = (_pathMode & PATH_HORIZONTAL_ONLY) > 0
		Local verticalOnly:Bool = (_pathMode & PATH_VERTICAL_ONLY) > 0

		If (horizontalOnly) Then
			If (Abs(deltaX) < pathSpeed * FlxG.elapsed) node = _AdvancePath()

		ElseIf (verticalOnly) Then
			If (Abs(deltaY) < pathSpeed * FlxG.elapsed) node = _AdvancePath()

		Else
			If (Sqrt(deltaX * deltaX + deltaY * deltaY) < pathSpeed * FlxG.elapsed) Then
				node = _AdvancePath()
			End If
		End If

		If (pathSpeed <> 0) Then
			_point.x = x + width * .5
			_point.y = y + height * .5
			If (horizontalOnly Or _point.y = node.y) Then
				If (_point.x < node.x) Then
					velocity.x = pathSpeed
				Else
					velocity.x = -pathSpeed
				End if

				If (velocity.x < 0) Then
					pathAngle = -90
				Else
					pathAngle = 90
				End If

				If (Not horizontalOnly) velocity.y = 0

			ElseIf (verticalOnly Or _point.x = node.x) Then
				If (_point.y < node.y) Then
					velocity.y = pathSpeed
				Else
					velocity.y = -pathSpeed
				End If

				If (velocity.y < 0) Then
					pathAngle = 0
				Else
					pathAngle = 180
				End If

				If (Not verticalOnly) velocity.x = 0

			Else
				pathAngle = FlxU.GetAngle(_point, node)
				FlxU.RotatePoint(0, pathSpeed, 0, 0, pathAngle, velocity)
			End If

			If (_pathRotate) Then
				angularVelocity = 0
				angularAcceleration = 0
				angle = pathAngle
			End If
		End If
	End Method

End Class

Private
Class FlxObjectClass Implements FlxClass

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

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

End Class

Class FlxObjectYComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		Return FlxObject(lhs).y - FlxObject(rhs).y
	End Method

End Class

Class FlxObjectXComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		Return FlxObject(lhs).x - FlxObject(rhs).x
	End Method

End Class

Class FlxObjectSeparateInvoker

	Const SEAPARATE_X:Int = 0
	Const SEPARATE_Y:Int = 1

	Field target:FlxObject
	Field methodToInvoke:Int

	Method Invoke:Bool(object1:FlxObject, object2:FlxObject)
		If (methodToInvoke = SEAPARATE_X) Then
			target.SeparateX(object1, object2)
		Else
			target.SeparateY(object1, object2)
		End If
	End Method

End Class

Private
	Global _separateInvoker:FlxObjectSeparateInvoker = New FlxObjectSeparateInvoker()

#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


add SoundManager.(Pause|Resume)(|Music|Sounds) methods

Strict

Private

Import bono
Import mojo.audio

Public

Class SoundManager
    Private

    Field sounds:StringMap<Sound> = New StringMap<Sound>()
    Field files:StringMap<String> = New StringMap<String>()
    Field extensions:String[] = ["mp3"]
    Field loopEnabled:StringMap<Bool> = New StringMap<Bool>()
    Field musicSounds:StringMap<Bool> = New StringMap<Bool>()
    Field lastChannel:Int
    Const CHANNELS:Int = 9

    Public

    Field failOnLoadingError:Bool = True

    Method Add:Void(key:String, file:String)
        If files.Contains(key) Then Error("Key " + key + " already used")
        files.Set(key, file)
    End

    Method AddMusic:Void(key:String, file:String)
        Add(key, file)
        musicSounds.Set(key, True)
    End

    Method PreloadAll:Void()
        Local tmpSound:Sound
        For Local node:map.Node<String, String> = EachIn files
            If sounds.Contains(node.Key()) Then Continue
            If musicSounds.Contains(node.Key()) Then Continue

            tmpSound = LoadSound(node.Value())
            If tmpSound = Null Then Continue

            sounds.Set(node.Key(), LoadSound(node.Value()))
        End
    End

    Method LoopEnable:Void(key:String)
        loopEnabled.Set(key, True)
    End

    Method LoopDisable:Void(key:String)
        loopEnabled.Remove(key)
    End

    Method Play:Void(key:String)
        If musicSounds.Contains(key)
            PlayMusic(key)
        Else
            PlaySound(key)
        End
    End

    Method Pause:Void()
        PauseMusic()
        PauseSounds()
    End

    Method PauseMusic:Void()
        audio.PauseMusic()
    End

    Method PauseSounds:Void()
        For Local i := 0 To CHANNELS
            PauseChannel(i)
        End
    End

    Method Resume:Void()
        ResumeMusic()
        ResumeSounds()
    End

    Method ResumeMusic:Void()
        audio.ResumeMusic()
    End

    Method ResumeSounds:Void()
        For Local i := 0 To CHANNELS
            ResumeChannel(i)
        End
    End

    Private

    Method PlayMusic:Void(key:String)
        Local isLoop:Bool = loopEnabled.Contains(key)
        Local file:String = files.Get(key)

        For Local idx:Int = 0 Until extensions.Length()
            audio.PlayMusic(file + "." + extensions[idx], isLoop)
            If MusicState() = 1 Then Return
        End
    End

    Method PlaySound:Void(key:String)
        If Not sounds.Contains(key) Then Return

        Local channel:Int = GetFreeChannel()
        If channel = -1 Then Return

        audio.PlaySound(sounds.Get(key), channel, loopEnabled.Contains(key))
    End

    Method GetFreeChannel:Int()
        Local channelsChecked:Int = 0
        Local channelId:Int = lastChannel

        Repeat
            channelId += 1
            If channelId >= CHANNELS Then channelId = 0

            ' Skip the ChannelState check on Android, because:
            ' > Android: ChannelState always returns -1, ie: 'unknown'. Sounds
            ' >          to be used with PlaySound must be less than 1MB in
            ' >          length. Longer sounds can be played using the music
            ' >          commands.
            ' Source: http://blitz-wiki.appspot.com/mojo.audio
            If Target.IS_ANDROID Or ChannelState(channelId) = 0
                lastChannel = channelId
                Return channelId
            End

            channelsChecked += 1
        Until channelsChecked >= CHANNELS

        Return -1
    End

    Method LoadSound:Sound(file:String)
        Local tmpSound:Sound
        For Local idx:Int = 0 Until extensions.Length()
            tmpSound = audio.LoadSound(file + "." + extensions[idx])
            If Not (tmpSound = Null) Then Return tmpSound
        End

        If failOnLoadingError Then Error("Unable to load sound file: " + file)
        Return Null
    End
End

Strict

Private

Import bono
Import mojo.audio

Public

Class SoundManager
    Private

    Field sounds:StringMap<Sound> = New StringMap<Sound>()
    Field files:StringMap<String> = New StringMap<String>()
    Field extensions:String[] = ["mp3"]
    Field loopEnabled:StringMap<Bool> = New StringMap<Bool>()
    Field musicSounds:StringMap<Bool> = New StringMap<Bool>()
    Field lastChannel:Int
    Const CHANNELS:Int = 9

    Public

    Field failOnLoadingError:Bool = True

    Method Add:Void(key:String, file:String)
        If files.Contains(key) Then Error("Key " + key + " already used")
        files.Set(key, file)
    End

    Method AddMusic:Void(key:String, file:String)
        Add(key, file)
        musicSounds.Set(key, True)
    End

    Method PreloadAll:Void()
        Local tmpSound:Sound
        For Local node:map.Node<String, String> = EachIn files
            If sounds.Contains(node.Key()) Then Continue
            If musicSounds.Contains(node.Key()) Then Continue

            tmpSound = LoadSound(node.Value())
            If tmpSound = Null Then Continue

            sounds.Set(node.Key(), LoadSound(node.Value()))
        End
    End

    Method LoopEnable:Void(key:String)
        loopEnabled.Set(key, True)
    End

    Method LoopDisable:Void(key:String)
        loopEnabled.Remove(key)
    End

    Method Play:Void(key:String)
        If musicSounds.Contains(key)
            PlayMusic(key)
        Else
            PlaySound(key)
        End
    End

    Private

    Method PlayMusic:Void(key:String)
        Local isLoop:Bool = loopEnabled.Contains(key)
        Local file:String = files.Get(key)

        For Local idx:Int = 0 Until extensions.Length()
            audio.PlayMusic(file + "." + extensions[idx], isLoop)
            If MusicState() = 1 Then Return
        End
    End

    Method PlaySound:Void(key:String)
        If Not sounds.Contains(key) Then Return

        Local channel:Int = GetFreeChannel()
        If channel = -1 Then Return

        audio.PlaySound(sounds.Get(key), channel, loopEnabled.Contains(key))
    End

    Method GetFreeChannel:Int()
        Local channelsChecked:Int = 0
        Local channelId:Int = lastChannel

        Repeat
            channelId += 1
            If channelId >= CHANNELS Then channelId = 0

            ' Skip the ChannelState check on Android, because:
            ' > Android: ChannelState always returns -1, ie: 'unknown'. Sounds
            ' >          to be used with PlaySound must be less than 1MB in
            ' >          length. Longer sounds can be played using the music
            ' >          commands.
            ' Source: http://blitz-wiki.appspot.com/mojo.audio
            If Target.IS_ANDROID Or ChannelState(channelId) = 0
                lastChannel = channelId
                Return channelId
            End

            channelsChecked += 1
        Until channelsChecked >= CHANNELS

        Return -1
    End

    Method LoadSound:Sound(file:String)
        Local tmpSound:Sound
        For Local idx:Int = 0 Until extensions.Length()
            tmpSound = audio.LoadSound(file + "." + extensions[idx])
            If Not (tmpSound = Null) Then Return tmpSound
        End

        If failOnLoadingError Then Error("Unable to load sound file: " + file)
        Return Null
    End
End


Added support of negative ranges for AddAnimation

Strict

Import mojo

Import flxextern
Import flxpoint
Import flxobject
Import flxcamera
Import flxg

Import system.flxanim
Import system.flxcolor

Import "data/default_flx.png"

Class FlxSprite Extends FlxObject

	Global ClassObject:Object

	Field origin:FlxPoint

	Field offset:FlxPoint

	Field scale:FlxPoint

	Field blend:Int

	Field finished:Bool

	Field frameWidth:Int

	Field frameHeight:Int

	Field frames:Int

	Field dirty:Bool

	Field _color:FlxColor

	Field _mixedColor:FlxColor

	Field _camera:FlxCamera

Private
	Global _GraphicLoader:FlxGraphicLoader = New FlxGraphicLoader()

	Global _Matrix:Float[6]

	Field _animations:StringMap<FlxAnim>

	Field _flipped:Bool

	Field _flipNeeded:Bool

	Field _curAnim:FlxAnim

	Field _curFrame:Int

	Field _curIndex:Int

	Field _frameTimer:Float

	Field _paused:Bool

	Field _callback:FlxAnimationListener

	Field _facing:Int

	Field _alpha:Float

	Field _bakedRotation:Float

	Field _pixels:Image

	Field _halfWidth:Float

	Field _halfHeight:Float

	Field _renderer:FlxSpriteRenderer

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

		offset = New FlxPoint()
		origin = New FlxPoint()

		scale = New FlxPoint(1.0, 1.0)
		_alpha = 1
		_color = New FlxColor(FlxG.WHITE)
		blend = AlphaBlend
		_cameras = Null

		finished = False
		_facing = RIGHT
		_animations = New StringMap<FlxAnim>
		_flipped = False
		_curAnim = Null
		_curFrame = 0
		_curIndex = 0
		_frameTimer = 0
		_paused = False

		_callback = Null

		_mixedColor = New FlxColor(FlxG.WHITE)

		If (simpleGraphic.Length() = 0)
			simpleGraphic = "default" + FlxG.DATA_SUFFIX
		End If

		LoadGraphic(simpleGraphic)
	End Method

	Method Destroy:Void()
		If (_animations <> Null) Then
			_animations.Clear()
			_animations = Null
		End If

		offset = Null
		origin = Null
		scale = Null
		_curAnim = Null
		_callback = Null
		_color = Null
		_mixedColor = Null
		_camera = Null
		_renderer = Null

		Super.Destroy()
	End Method

	Method LoadGraphic:FlxSprite(graphic:String, animated:Bool = False, reverse:Bool = False, width:Int = 0, height:Int = 0, unique:Bool = False)
		_bakedRotation = 0

		_GraphicLoader.name = graphic
		_GraphicLoader.animated = animated
		_GraphicLoader.width = width
		_GraphicLoader.height = height

		_pixels = FlxG.AddBitmap(graphic, _GraphicLoader, unique)

		_flipped = reverse

		Self.width = _pixels.Width()
		frameWidth = Self.width

		Self.height = _pixels.Height()
		frameHeight = Self.height

		ResetHelpers()

		Return Self
	End Method

	Method LoadRotatedGraphic:FlxSprite(graphic:String, rotations:Int = 16, frame:Int = -1)
		Error "LoadRotatedGraphic not currenlty support in " + FlxG.LIBRARY_NAME
		'TODO
		Return Null
	End Method

	Method MakeGraphic:FlxSprite(width:Int, height:Int, color:Int = FlxG.WHITE, unique:Bool = False, key:String = "")
		_pixels = FlxG.CreateBitmap(width, height, color, unique, key)
		_bakedRotation = 0

		Self.width = width
		frameWidth = width
		Self.height = height
		frameHeight = height

		ResetHelpers()

		Return Self
	End Method

	Method PostUpdate:Void()
		Super.PostUpdate()
		_UpdateAnimation()
	End Method

	Method Draw:Void()
		If (_flickerTimer <> 0) Then
			_flicker = Not _flicker
			If (_flicker) Return
		End If

		_camera = FlxG._CurrentCamera

		If (_cameras <> Null And Not _cameras.Contains(_camera)) Return
		If (Not OnScreen(_camera)) Return

		If (dirty) _CalcFrame()

		If (FlxG._LastDrawingBlend <> blend) Then
			SetBlend(blend)
			FlxG._LastDrawingBlend = blend
		End If

		_point.x = x - int(_camera.scroll.x * scrollFactor.x) - offset.x
		_point.y = y - int(_camera.scroll.y * scrollFactor.y) - offset.y

		If (_point.x > 0) Then
			_point.x += 0.0000001
		Else
			_point.x -= 0.0000001
		End If

		If (_point.y > 0) Then
			_point.y += 0.0000001
		Else
			_point.y -= 0.0000001
		End If

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

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

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

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

		If ((angle = 0 Or _bakedRotation > 0) And scale.x = 1 And scale.y = 1) Then
			If ( Not _flipNeeded) Then
				If (_renderer = Null) Then
					DrawImage(_pixels, _point.x, _point.y, _curIndex)
				Else
					_renderer.OnSpriteRender(_point.x, _point.y)
				End If
			Else
				PushMatrix()
					Transform(-1, 0, 0, 1, _point.x + _halfWidth, _point.y + _halfHeight)

					If (_renderer = Null) Then
						DrawImage(_pixels, -_halfWidth, -_halfHeight, _curIndex)
					Else
						_renderer.OnSpriteRender(-_halfWidth, -_halfHeight)
					End If
				PopMatrix()
			End If
		Else
			PushMatrix()
				'Translate
				_Matrix[4] = _point.x + origin.x
				_Matrix[5] = _point.y + origin.y

				'Scale
				_Matrix[0] = scale.x
				_Matrix[3] = scale.y

				'Rotate
				If (angle <> 0 And _bakedRotation = 0) Then
					Local sin:Float = Sin(angle)
					Local cos:Float = Cos(angle)

					_Matrix[1] = sin * _Matrix[0]
					_Matrix[2] = -sin * _Matrix[3]
					_Matrix[0] *= cos
					_Matrix[3] *= cos
				Else
					_Matrix[1] = 0
					_Matrix[2] = 0
				End If

				Transform(_Matrix[0], _Matrix[1], _Matrix[2], _Matrix[3], _Matrix[4], _Matrix[5])

				If (_flipNeeded) Then
					Transform(-1, 0, 0, 1, _halfWidth - origin.x, _halfHeight - origin.y)
				End If

				If (_renderer = Null) Then
					DrawImage(_pixels, -origin.x, -origin.y, _curIndex)
				Else
					_renderer.OnSpriteRender(-origin.x, -origin.y)
				End If
			PopMatrix()
		End If

	#If FLX_DEBUG_ENABLED = "1"
		_VisibleCount += 1;
		If (FlxG.VisualDebug And Not ignoreDrawDebug) DrawDebug(_camera);
	#End
	End Method

	Method DrawFrame:Void(force:Bool = False)
		If (force Or dirty) _CalcFrame()
	End Method

	Method AddAnimation:FlxAnim(name:String, frames:Int[], frameRate:Float = 0, looped:Bool = True)
		Local anim:FlxAnim = New FlxAnim(name, frames, frameRate, looped)
		_animations.Set(name, anim)

		Return anim
	End Method

	Method AddAnimation:FlxAnim(name:String, startFrame:Int, endFrame:Int, frameRate:Float = 0, looped:Bool = True)
		Local i:Int = 0
		Local l:Int = Abs(endFrame - startFrame) + 1
		Local frames:Int[] = New Int[l]

		Local dir:Int = 1
		If (endFrame < startFrame) dir = -1

		While (i < l)
			frames[i] = startFrame + dir * i
			i += 1
		Wend

		Return AddAnimation(name, frames, frameRate, looped)
	End Method

	Method AddAnimation:FlxAnim(name:String, frameRate:Float = 0, looped:Bool = True)
		If (_pixels = Null) Return Null
		Return AddAnimation(name, 0, _pixels.Frames() -1, frameRate, looped)
	End Method

	Method AddAnimationCallback:Void(animationCallback:FlxAnimationListener)
		_callback = animationCallback
	End Method

	Method ClearAnimationCallback:Void()
		_callback = Null
	End Method

	Method GetAnimation:FlxAnim(animName:String)
		Return _animations.Get(animName)
	End Method

	Method GetAnimation:FlxAnim()
		Return _curAnim
	End Method

	Method Play:Void(animName:String, force:Bool = False)
		If (Not force And _curAnim <> Null And animName = _curAnim.name And( _curAnim.looped Or Not finished )) Return

		_curFrame = 0
		_curIndex = 0
		_frameTimer = 0
		_paused = False

		Local anim:FlxAnim = _animations.Get(animName)

		If (anim <> Null) Then
			_curAnim = anim

			If (_curAnim.delay <= 0) Then
				finished = True
			Else
				finished = False
			End If

			_curIndex = _curAnim.frames[_curFrame]
			dirty = True
			Return
		End If

		#If FLX_DEBUG_ENABLED = "1"
			FlxG.Log("WARNING: No animation called ~q" + animName + "~q")
		#End
	End Method

	Method Stop:Void()
		Frame = 0
	End Method

	Method Pause:Void()
		_paused = True
	End Method

	Method Resume:Void()
		_paused = False
	End Method

	Method RandomFrame:Void()
		_curAnim = Null

		If (_pixels <> Null) Then
			_curIndex = Int(FlxG.Random() * (_pixels.Frames() - 1))
		Else
			_curIndex = 0
		End If

		dirty = True
	End Method

	Method SetOriginToCorner:Void()
		origin.x = 0
		origin.y = 0
	End Method

	Method CenterOffsets:Void(adjustPosition:Bool = False)
		offset.x = (frameWidth - width) * .5
		offset.y = (frameHeight - height) * .5

		If (adjustPosition) Then
			x += offset.x
			y += offset.y
		End If
	End Method

	Method Pixels:Image() Property
		Return _pixels
	End Method

	Method Pixels:Void(pixels:Image) Property
		_pixels = pixels

		Local width:Int = 0
		Local height:Int = 0

		If (_pixels <> Null) Then
			width = pixels.Width()
			height = pixels.Height()
		End If

		If (_pixels <> Null Or _renderer = Null) Then
			Self.width = width
			Self.height = height
			frameWidth = width
			frameHeight = height
			ResetHelpers()
		End If
	End Method

	Method Facing:Int() Property
		Return _facing
	End Method

	Method Facing:Void(direction:Int) Property
		If (_facing <> direction) dirty = True
		_facing = direction

		_flipNeeded = (_flipped And _facing = LEFT)
	End Method

	Method Alpha:Float() Property
		Return _alpha
	End Method

	Method Alpha:Void(alpha:Float) Property
		If (alpha < 0) alpha = 0
		_alpha = alpha
	End Method

	Method Color:Int() Property
		Return _color.argb
	End Method

	Method Color:Void(color:Int) Property
		_color.SetRGB(color)
	End Method

	Method Frame:Int() Property
		Return _curIndex
	End Method

	Method Frame:Void(frame:Int) Property
		_curAnim = Null
		_curIndex = frame
		_paused = False
		dirty = True
	End Method

	Method OnScreen:Bool(camera:FlxCamera = Null)
		If (camera = Null) camera = FlxG.Camera
		GetScreenXY(_point, camera)

		_point.x = _point.x - offset.x
		_point.y = _point.y - offset.y

		If ((angle = 0 Or _bakedRotation > 0) And scale.x = 1 And scale.y = 1) Then
			Return _point.x + frameWidth > 0 And _point.x < camera.Width And _point.y + frameHeight > 0 And _point.y < camera.Height
		End If

		Local radius:Float = Sqrt(_halfWidth * _halfWidth + _halfHeight * _halfHeight) * Max(Abs(scale.x), Abs(scale.y))
		_point.x += _halfWidth
		_point.y += _halfHeight

		Return _point.x + radius > 0 And _point.x - radius < camera.Width And _point.y + radius > 0 And _point.y - radius < camera.Height
	End Method

	Method SetRenderer:Void(render:FlxSpriteRenderer)
		_renderer = render
		_renderer.OnSpriteBind(Self)

		frameWidth = width
		frameHeight = height

		ResetHelpers()
	End Method

	Method ClearRenderer:Void()
		Local renderer:FlxSpriteRenderer = _renderer
		_renderer = Null
		renderer.OnSpriteUnbind()

		frameWidth = width
		frameHeight = height

		ResetHelpers()
	End Method

	Method ResetHelpers:Void()
		_halfWidth = frameWidth * .5
		_halfHeight = frameHeight * .5
		origin.Make(_halfWidth, _halfHeight)

		If (_pixels <> Null) Then
			frames = _pixels.Frames()
		Else
			frames = 1
		End If

		_curIndex = 0
		_mixedColor.SetARGB(FlxG.WHITE)
	End Method

Private
	Method _UpdateAnimation:Void()
		If (_bakedRotation > 0) Then
			Local oldIndex:Int = _curIndex
			Local angleHelper:Int = angle Mod 360

			If (angleHelper < 0) angleHelper += 360
			_curIndex = angleHelper / _bakedRotation + 0.5

			If (oldIndex <> _curIndex) _CalcFrame()
			Return
		End If

		If (_paused) Return

		If (_curAnim <> Null And _curAnim.delay > 0 And (_curAnim.looped Or Not finished))
			_frameTimer += FlxG.Elapsed
			While (_frameTimer > _curAnim.delay)
				_frameTimer -= _curAnim.delay

				If (_curFrame = _curAnim.frames.Length() - 1) Then
					If (_curAnim.looped) _curFrame = 0
					finished = True
				Else
					_curFrame += 1
				End If

				_curIndex = _curAnim.frames[_curFrame]
				dirty = True
			Wend
		End If

		If (dirty) _CalcFrame()
	End Method

	Method _CalcFrame:Void()
		If (_callback <> Null) Then
			If (_curAnim <> Null) Then
				_callback.OnAnimationFrame(_curAnim, _curFrame, _curIndex)
			Else
				_callback.OnAnimationFrame(Null, _curFrame, _curIndex)
			End If
		End If
		dirty = False
	End Method

End Class

Interface FlxSpriteRenderer

	Method OnSpriteBind:Void(sprite:FlxSprite)

	Method OnSpriteUnbind:Void()

	Method OnSpriteRender:Void(x:Float, y:Float)

End Interface

Class PrimitiveSpriteRenderer Implements FlxSpriteRenderer Abstract

	Method OnSpriteBind:Void(sprite:FlxSprite)
		_sprite = sprite
		_sprite.Pixels = Null
	End Method

	Method OnSpriteUnbind:Void()
		_sprite.Pixels = Null
		_sprite = Null
	End Method

	Method OnSpriteRender:Void(x:Float, y:Float) Abstract

	Private

	Field _sprite:FlxSprite

End Class

Class RectSpriteRenderer Extends PrimitiveSpriteRenderer

	Method OnSpriteRender:Void(x:Float, y:Float)
		DrawRect(x, y, _sprite.frameWidth, _sprite.frameHeight)
	End Method

End Class

Class OvalSpriteRenderer Extends PrimitiveSpriteRenderer

	Method OnSpriteRender:Void(x:Float, y:Float)
		DrawOval(x, y, _sprite.frameWidth, _sprite.frameHeight)
	End Method

End Class

Private
Class FlxGraphicLoader Extends FlxResourceLoader<Image>

	Field name:String
	Field animated:Bool
	Field width:Float
	Field height:Float

	Method Load:Image(name:String)
		Local image:Image = LoadImage(FlxAssetsManager.GetImagePath(Self.name))

		If (Not animated) Then
			Return image
		Else
			Local frames:Int

			If (width = 0 And height = 0) Then
				width = image.Height()
			ElseIf (width = 0) Then
				width = height
			ElseIf (height = 0) Then
				height = width
			End If

			If (height = 0) Then
				frames = Ceil(image.Width() / width)
				height = width
			Else
				frames = Ceil((image.Width() * image.Height()) / (width * height))
			End If

			Return image.GrabImage(0, 0, width, height, frames)
		End If
	End Method

End Class

Strict

Import mojo

Import flxextern
Import flxpoint
Import flxobject
Import flxcamera
Import flxg

Import system.flxanim
Import system.flxcolor

Import "data/default_flx.png"

Class FlxSprite Extends FlxObject

	Global ClassObject:Object

	Field origin:FlxPoint

	Field offset:FlxPoint

	Field scale:FlxPoint

	Field blend:Int

	Field finished:Bool

	Field frameWidth:Int

	Field frameHeight:Int

	Field frames:Int

	Field dirty:Bool

	Field _color:FlxColor

	Field _mixedColor:FlxColor

	Field _camera:FlxCamera

Private
	Global _GraphicLoader:FlxGraphicLoader = New FlxGraphicLoader()

	Global _Matrix:Float[6]

	Field _animations:StringMap<FlxAnim>

	Field _flipped:Bool

	Field _flipNeeded:Bool

	Field _curAnim:FlxAnim

	Field _curFrame:Int

	Field _curIndex:Int

	Field _frameTimer:Float

	Field _paused:Bool

	Field _callback:FlxAnimationListener

	Field _facing:Int

	Field _alpha:Float

	Field _bakedRotation:Float

	Field _pixels:Image

	Field _halfWidth:Float

	Field _halfHeight:Float

	Field _renderer:FlxSpriteRenderer

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

		offset = New FlxPoint()
		origin = New FlxPoint()

		scale = New FlxPoint(1.0, 1.0)
		_alpha = 1
		_color = New FlxColor(FlxG.WHITE)
		blend = AlphaBlend
		_cameras = Null

		finished = False
		_facing = RIGHT
		_animations = New StringMap<FlxAnim>
		_flipped = False
		_curAnim = Null
		_curFrame = 0
		_curIndex = 0
		_frameTimer = 0
		_paused = False

		_callback = Null

		_mixedColor = New FlxColor(FlxG.WHITE)

		If (simpleGraphic.Length() = 0)
			simpleGraphic = "default" + FlxG.DATA_SUFFIX
		End If

		LoadGraphic(simpleGraphic)
	End Method

	Method Destroy:Void()
		If (_animations <> Null) Then
			_animations.Clear()
			_animations = Null
		End If

		offset = Null
		origin = Null
		scale = Null
		_curAnim = Null
		_callback = Null
		_color = Null
		_mixedColor = Null
		_camera = Null
		_renderer = Null

		Super.Destroy()
	End Method

	Method LoadGraphic:FlxSprite(graphic:String, animated:Bool = False, reverse:Bool = False, width:Int = 0, height:Int = 0, unique:Bool = False)
		_bakedRotation = 0

		_GraphicLoader.name = graphic
		_GraphicLoader.animated = animated
		_GraphicLoader.width = width
		_GraphicLoader.height = height

		_pixels = FlxG.AddBitmap(graphic, _GraphicLoader, unique)

		_flipped = reverse

		Self.width = _pixels.Width()
		frameWidth = Self.width

		Self.height = _pixels.Height()
		frameHeight = Self.height

		ResetHelpers()

		Return Self
	End Method

	Method LoadRotatedGraphic:FlxSprite(graphic:String, rotations:Int = 16, frame:Int = -1)
		Error "LoadRotatedGraphic not currenlty support in " + FlxG.LIBRARY_NAME
		'TODO
		Return Null
	End Method

	Method MakeGraphic:FlxSprite(width:Int, height:Int, color:Int = FlxG.WHITE, unique:Bool = False, key:String = "")
		_pixels = FlxG.CreateBitmap(width, height, color, unique, key)
		_bakedRotation = 0

		Self.width = width
		frameWidth = width
		Self.height = height
		frameHeight = height

		ResetHelpers()

		Return Self
	End Method

	Method PostUpdate:Void()
		Super.PostUpdate()
		_UpdateAnimation()
	End Method

	Method Draw:Void()
		If (_flickerTimer <> 0) Then
			_flicker = Not _flicker
			If (_flicker) Return
		End If

		_camera = FlxG._CurrentCamera

		If (_cameras <> Null And Not _cameras.Contains(_camera)) Return
		If (Not OnScreen(_camera)) Return

		If (dirty) _CalcFrame()

		If (FlxG._LastDrawingBlend <> blend) Then
			SetBlend(blend)
			FlxG._LastDrawingBlend = blend
		End If

		_point.x = x - int(_camera.scroll.x * scrollFactor.x) - offset.x
		_point.y = y - int(_camera.scroll.y * scrollFactor.y) - offset.y

		If (_point.x > 0) Then
			_point.x += 0.0000001
		Else
			_point.x -= 0.0000001
		End If

		If (_point.y > 0) Then
			_point.y += 0.0000001
		Else
			_point.y -= 0.0000001
		End If

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

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

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

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

		If ((angle = 0 Or _bakedRotation > 0) And scale.x = 1 And scale.y = 1) Then
			If ( Not _flipNeeded) Then
				If (_renderer = Null) Then
					DrawImage(_pixels, _point.x, _point.y, _curIndex)
				Else
					_renderer.OnSpriteRender(_point.x, _point.y)
				End If
			Else
				PushMatrix()
					Transform(-1, 0, 0, 1, _point.x + _halfWidth, _point.y + _halfHeight)

					If (_renderer = Null) Then
						DrawImage(_pixels, -_halfWidth, -_halfHeight, _curIndex)
					Else
						_renderer.OnSpriteRender(-_halfWidth, -_halfHeight)
					End If
				PopMatrix()
			End If
		Else
			PushMatrix()
				'Translate
				_Matrix[4] = _point.x + origin.x
				_Matrix[5] = _point.y + origin.y

				'Scale
				_Matrix[0] = scale.x
				_Matrix[3] = scale.y

				'Rotate
				If (angle <> 0 And _bakedRotation = 0) Then
					Local sin:Float = Sin(angle)
					Local cos:Float = Cos(angle)

					_Matrix[1] = sin * _Matrix[0]
					_Matrix[2] = -sin * _Matrix[3]
					_Matrix[0] *= cos
					_Matrix[3] *= cos
				Else
					_Matrix[1] = 0
					_Matrix[2] = 0
				End If

				Transform(_Matrix[0], _Matrix[1], _Matrix[2], _Matrix[3], _Matrix[4], _Matrix[5])

				If (_flipNeeded) Then
					Transform(-1, 0, 0, 1, _halfWidth - origin.x, _halfHeight - origin.y)
				End If

				If (_renderer = Null) Then
					DrawImage(_pixels, -origin.x, -origin.y, _curIndex)
				Else
					_renderer.OnSpriteRender(-origin.x, -origin.y)
				End If
			PopMatrix()
		End If

	#If FLX_DEBUG_ENABLED = "1"
		_VisibleCount += 1;
		If (FlxG.VisualDebug And Not ignoreDrawDebug) DrawDebug(_camera);
	#End
	End Method

	Method DrawFrame:Void(force:Bool = False)
		If (force Or dirty) _CalcFrame()
	End Method

	Method AddAnimation:FlxAnim(name:String, frames:Int[], frameRate:Float = 0, looped:Bool = True)
		Local anim:FlxAnim = New FlxAnim(name, frames, frameRate, looped)
		_animations.Set(name, anim)

		Return anim
	End Method

	Method AddAnimation:FlxAnim(name:String, startFrame:Int, endFrame:Int, frameRate:Float = 0, looped:Bool = True)
		Local i:Int = 0
		Local l:Int = endFrame - startFrame + 1
		Local frames:Int[] = New Int[l]

		While (i < l)
			frames[i] = startFrame + i
			i += 1
		Wend

		Return AddAnimation(name, frames, frameRate, looped)
	End Method

	Method AddAnimation:FlxAnim(name:String, frameRate:Float = 0, looped:Bool = True)
		If (_pixels = Null) Return Null
		Return AddAnimation(name, 0, _pixels.Frames() -1, frameRate, looped)
	End Method

	Method AddAnimationCallback:Void(animationCallback:FlxAnimationListener)
		_callback = animationCallback
	End Method

	Method ClearAnimationCallback:Void()
		_callback = Null
	End Method

	Method GetAnimation:FlxAnim(animName:String)
		Return _animations.Get(animName)
	End Method

	Method GetAnimation:FlxAnim()
		Return _curAnim
	End Method

	Method Play:Void(animName:String, force:Bool = False)
		If (Not force And _curAnim <> Null And animName = _curAnim.name And( _curAnim.looped Or Not finished )) Return

		_curFrame = 0
		_curIndex = 0
		_frameTimer = 0
		_paused = False

		Local anim:FlxAnim = _animations.Get(animName)

		If (anim <> Null) Then
			_curAnim = anim

			If (_curAnim.delay <= 0) Then
				finished = True
			Else
				finished = False
			End If

			_curIndex = _curAnim.frames[_curFrame]
			dirty = True
			Return
		End If

		#If FLX_DEBUG_ENABLED = "1"
			FlxG.Log("WARNING: No animation called ~q" + animName + "~q")
		#End
	End Method

	Method Stop:Void()
		Frame = 0
	End Method

	Method Pause:Void()
		_paused = True
	End Method

	Method Resume:Void()
		_paused = False
	End Method

	Method RandomFrame:Void()
		_curAnim = Null

		If (_pixels <> Null) Then
			_curIndex = Int(FlxG.Random() * (_pixels.Frames() - 1))
		Else
			_curIndex = 0
		End If

		dirty = True
	End Method

	Method SetOriginToCorner:Void()
		origin.x = 0
		origin.y = 0
	End Method

	Method CenterOffsets:Void(adjustPosition:Bool = False)
		offset.x = (frameWidth - width) * .5
		offset.y = (frameHeight - height) * .5

		If (adjustPosition) Then
			x += offset.x
			y += offset.y
		End If
	End Method

	Method Pixels:Image() Property
		Return _pixels
	End Method

	Method Pixels:Void(pixels:Image) Property
		_pixels = pixels

		Local width:Int = 0
		Local height:Int = 0

		If (_pixels <> Null) Then
			width = pixels.Width()
			height = pixels.Height()
		End If

		If (_pixels <> Null Or _renderer = Null) Then
			Self.width = width
			Self.height = height
			frameWidth = width
			frameHeight = height
			ResetHelpers()
		End If
	End Method

	Method Facing:Int() Property
		Return _facing
	End Method

	Method Facing:Void(direction:Int) Property
		If (_facing <> direction) dirty = True
		_facing = direction

		_flipNeeded = (_flipped And _facing = LEFT)
	End Method

	Method Alpha:Float() Property
		Return _alpha
	End Method

	Method Alpha:Void(alpha:Float) Property
		If (alpha < 0) alpha = 0
		_alpha = alpha
	End Method

	Method Color:Int() Property
		Return _color.argb
	End Method

	Method Color:Void(color:Int) Property
		_color.SetRGB(color)
	End Method

	Method Frame:Int() Property
		Return _curIndex
	End Method

	Method Frame:Void(frame:Int) Property
		_curAnim = Null
		_curIndex = frame
		_paused = False
		dirty = True
	End Method

	Method OnScreen:Bool(camera:FlxCamera = Null)
		If (camera = Null) camera = FlxG.Camera
		GetScreenXY(_point, camera)

		_point.x = _point.x - offset.x
		_point.y = _point.y - offset.y

		If ((angle = 0 Or _bakedRotation > 0) And scale.x = 1 And scale.y = 1) Then
			Return _point.x + frameWidth > 0 And _point.x < camera.Width And _point.y + frameHeight > 0 And _point.y < camera.Height
		End If

		Local radius:Float = Sqrt(_halfWidth * _halfWidth + _halfHeight * _halfHeight) * Max(Abs(scale.x), Abs(scale.y))
		_point.x += _halfWidth
		_point.y += _halfHeight

		Return _point.x + radius > 0 And _point.x - radius < camera.Width And _point.y + radius > 0 And _point.y - radius < camera.Height
	End Method

	Method SetRenderer:Void(render:FlxSpriteRenderer)
		_renderer = render
		_renderer.OnSpriteBind(Self)

		frameWidth = width
		frameHeight = height

		ResetHelpers()
	End Method

	Method ClearRenderer:Void()
		Local renderer:FlxSpriteRenderer = _renderer
		_renderer = Null
		renderer.OnSpriteUnbind()

		frameWidth = width
		frameHeight = height

		ResetHelpers()
	End Method

	Method ResetHelpers:Void()
		_halfWidth = frameWidth * .5
		_halfHeight = frameHeight * .5
		origin.Make(_halfWidth, _halfHeight)

		If (_pixels <> Null) Then
			frames = _pixels.Frames()
		Else
			frames = 1
		End If

		_curIndex = 0
		_mixedColor.SetARGB(FlxG.WHITE)
	End Method

Private
	Method _UpdateAnimation:Void()
		If (_bakedRotation > 0) Then
			Local oldIndex:Int = _curIndex
			Local angleHelper:Int = angle Mod 360

			If (angleHelper < 0) angleHelper += 360
			_curIndex = angleHelper / _bakedRotation + 0.5

			If (oldIndex <> _curIndex) _CalcFrame()
			Return
		End If

		If (_paused) Return

		If (_curAnim <> Null And _curAnim.delay > 0 And (_curAnim.looped Or Not finished))
			_frameTimer += FlxG.Elapsed
			While (_frameTimer > _curAnim.delay)
				_frameTimer -= _curAnim.delay

				If (_curFrame = _curAnim.frames.Length() - 1) Then
					If (_curAnim.looped) _curFrame = 0
					finished = True
				Else
					_curFrame += 1
				End If

				_curIndex = _curAnim.frames[_curFrame]
				dirty = True
			Wend
		End If

		If (dirty) _CalcFrame()
	End Method

	Method _CalcFrame:Void()
		If (_callback <> Null) Then
			If (_curAnim <> Null) Then
				_callback.OnAnimationFrame(_curAnim, _curFrame, _curIndex)
			Else
				_callback.OnAnimationFrame(Null, _curFrame, _curIndex)
			End If
		End If
		dirty = False
	End Method

End Class

Interface FlxSpriteRenderer

	Method OnSpriteBind:Void(sprite:FlxSprite)

	Method OnSpriteUnbind:Void()

	Method OnSpriteRender:Void(x:Float, y:Float)

End Interface

Class PrimitiveSpriteRenderer Implements FlxSpriteRenderer Abstract

	Method OnSpriteBind:Void(sprite:FlxSprite)
		_sprite = sprite
		_sprite.Pixels = Null
	End Method

	Method OnSpriteUnbind:Void()
		_sprite.Pixels = Null
		_sprite = Null
	End Method

	Method OnSpriteRender:Void(x:Float, y:Float) Abstract

	Private

	Field _sprite:FlxSprite

End Class

Class RectSpriteRenderer Extends PrimitiveSpriteRenderer

	Method OnSpriteRender:Void(x:Float, y:Float)
		DrawRect(x, y, _sprite.frameWidth, _sprite.frameHeight)
	End Method

End Class

Class OvalSpriteRenderer Extends PrimitiveSpriteRenderer

	Method OnSpriteRender:Void(x:Float, y:Float)
		DrawOval(x, y, _sprite.frameWidth, _sprite.frameHeight)
	End Method

End Class

Private
Class FlxGraphicLoader Extends FlxResourceLoader<Image>

	Field name:String
	Field animated:Bool
	Field width:Float
	Field height:Float

	Method Load:Image(name:String)
		Local image:Image = LoadImage(FlxAssetsManager.GetImagePath(Self.name))

		If (Not animated) Then
			Return image
		Else
			Local frames:Int

			If (width = 0 And height = 0) Then
				width = image.Height()
			ElseIf (width = 0) Then
				width = height
			ElseIf (height = 0) Then
				height = width
			End If

			If (height = 0) Then
				frames = Ceil(image.Width() / width)
				height = width
			Else
				frames = Ceil((image.Width() * image.Height()) / (width * height))
			End If

			Return image.GrabImage(0, 0, width, height, frames)
		End If
	End Method

End Class


Highscore now survives "," in the score key

Strict

Private

Import mojo
Import score

Public

Class Highscore<T>
    Private

    Field objects:List<Score<T>> = New List<Score<T>>()
    Field maxCount_:Int

    Public

    Method maxCount:Int() Property
        Return maxCount_
    End

    Method New(maxCount:Int)
        maxCount_ = maxCount
    End

    Method Add:Void(key:String, value:T)
        objects.AddLast(New Score<T>(key, value))
        Sort()
        SizeTrim()
    End

    Method Clear:Void()
        objects.Clear()
    End

    Method Save:Void()
        SaveState(ToString())
    End

    Method Load:Void()
        FromString(LoadState())
    End

    Method Count:Int()
        Return objects.Count()
    End

    Method FromString:Void(input:String)
        objects.Clear()
        Local key:String
        Local value:T
        Local splitted:String[] = input.Split(",")

        For Local count:Int = 0 To splitted.Length() - 2 Step 2
            key = splitted[count].Replace("[COMMA]", ",")
            value = T(splitted[count + 1])
            objects.AddLast(New Score<T>(key, value))
        End

        Sort()
    End

    Method ToString:String()
        Local result:String
        For Local score:Score<T> = EachIn Self
            result += score.key.Replace(",", "[COMMA]") + "," + score.value + ","
        End
        Return result
    End

    Method ObjectEnumerator:list.Enumerator<Score<T>>()
        Return objects.ObjectEnumerator()
    End

    Private

    Method SizeTrim:Void()
        While objects.Count() > maxCount_
            objects.RemoveLast()
        End
    End

    Method Sort:Void()
        If objects.Count() < 2 Then Return

        Local newList:List<Score<T>> = New List<Score<T>>()
        Local current:Score<T>

        While objects.Count() > 0
            current = objects.First()
            For Local check:Score<T> = EachIn objects
                If check.value <= current.value
                    current = check
                End
            End

            newList.AddFirst(current)
            objects.Remove(current)
        End

        objects.Clear()
        objects = newList
    End
End

Class IntHighscore Extends Highscore<Int>
    Method New(maxCount:Int)
        Super.New(maxCount)
    End
End

Class FloatHighscore Extends Highscore<Float>
    Method New(maxCount:Int)
        Super.New(maxCount)
    End
End

Strict

Private

Import mojo
Import score

Public

Class Highscore<T>
    Private

    Field objects:List<Score<T>> = New List<Score<T>>()
    Field maxCount_:Int

    Public

    Method maxCount:Int() Property
        Return maxCount_
    End

    Method New(maxCount:Int)
        maxCount_ = maxCount
    End

    Method Add:Void(key:String, value:T)
        objects.AddLast(New Score<T>(key, value))
        Sort()
        SizeTrim()
    End

    Method Clear:Void()
        objects.Clear()
    End

    Method Save:Void()
        SaveState(ToString())
    End

    Method Load:Void()
        FromString(LoadState())
    End

    Method Count:Int()
        Return objects.Count()
    End

    Method FromString:Void(input:String)
        objects.Clear()
        Local key:String
        Local value:T
        Local splitted:String[] = input.Split(",")

        For Local count:Int = 0 To splitted.Length() - 2 Step 2
            key = splitted[count]
            value = T(splitted[count + 1])
            objects.AddLast(New Score<T>(key, value))
        End

        Sort()
    End

    Method ToString:String()
        Local result:String
        For Local score:Score<T> = EachIn Self
            result += score.key + "," + score.value + ","
        End
        Return result
    End

    Method ObjectEnumerator:list.Enumerator<Score<T>>()
        Return objects.ObjectEnumerator()
    End

    Private

    Method SizeTrim:Void()
        While objects.Count() > maxCount_
            objects.RemoveLast()
        End
    End

    Method Sort:Void()
        If objects.Count() < 2 Then Return

        Local newList:List<Score<T>> = New List<Score<T>>()
        Local current:Score<T>

        While objects.Count() > 0
            current = objects.First()
            For Local check:Score<T> = EachIn objects
                If check.value <= current.value
                    current = check
                End
            End

            newList.AddFirst(current)
            objects.Remove(current)
        End

        objects.Clear()
        objects = newList
    End
End

Class IntHighscore Extends Highscore<Int>
    Method New(maxCount:Int)
        Super.New(maxCount)
    End
End

Class FloatHighscore Extends Highscore<Float>
    Method New(maxCount:Int)
        Super.New(maxCount)
    End
End