Methods Separate, SeparateX and SeparateY of FlxBobject now static

Strict

#rem
	header:This module contains the FlxObject class.
#end

Import flxextern
Import flxbasic
Import flxpoint
Import flxpath
Import flxgroup
Import flxcamera
Import flxtilemap
Import flxg
Import flxu

Import system.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 ClassObject:FlxClass = new FlxObjectClass()

	Global XComparator:FlxBasicComparator = new FlxObjectXComparator()

	Global YComparator: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

	Field _flicker:Bool

	Field _flickerTimer:Float

	Field _point:FlxPoint

Private
	Global _separateXCallback:FlxObjectSeparateX = New FlxObjectSeparateX()

	Global _separateYCallback:FlxObjectSeparateY = New FlxObjectSeparateY()

	Global _pZero:FlxPoint = New 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
		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)

		If (FlxG._lastDrawingColor <> _debugBoundingBoxColor.argb) Then
			SetColor(_debugBoundingBoxColor.r, _debugBoundingBoxColor.g, _debugBoundingBoxColor.b)
			FlxG._lastDrawingColor = _debugBoundingBoxColor.argb
		End If

		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)

		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

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

		Return separatedX Or separatedY
	End Function

	Function 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
			Return FlxTilemap(object1).OverlapsWithCallback(object2, _separateXCallback)
		End If

		If (FlxTilemap(object2) <> Null) Then
			Return FlxTilemap(object2).OverlapsWithCallback(object1, _separateXCallback, 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 Function

	Function 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
			Return FlxTilemap(object1).OverlapsWithCallback(object2, _separateYCallback)
		End If

		If (FlxTilemap(object2) <> Null) Then
			Return FlxTilemap(object2).OverlapsWithCallback(object1, _separateYCallback, 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 Function

	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
				End If

			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

		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:Object()
		Return New FlxObject()
	End Method

	Method InstanceOf:Bool(object:Object)
		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 FlxObjectSeparateX Implements FlxTilemapOverlapListener

	Method OnTilemapOverlap:Bool(object1:FlxObject, object2:FlxObject)
		FlxObject.SeparateX(object1, object2)
	End Method

End Class

Class FlxObjectSeparateY Implements FlxTilemapOverlapListener

	Method OnTilemapOverlap:Bool(object1:FlxObject, object2:FlxObject)
		FlxObject.SeparateY(object1, object2)
	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

Strict

#rem
	header:This module contains the FlxObject class.
#end

Import flxextern
Import flxbasic
Import flxpoint
Import flxpath
Import flxgroup
Import flxcamera
Import flxtilemap
Import flxg
Import flxu

Import system.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 ClassObject:FlxClass = new FlxObjectClass()

	Global XComparator:FlxBasicComparator = new FlxObjectXComparator()

	Global YComparator: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

	Field _flicker:Bool

	Field _flickerTimer:Float

	Field _point:FlxPoint

Private
	Global _separateXCallback:FlxObjectSeparateX = New FlxObjectSeparateX()

	Global _separateYCallback:FlxObjectSeparateY = New FlxObjectSeparateY()

	Global _pZero:FlxPoint = New 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
		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)

		If (FlxG._lastDrawingColor <> _debugBoundingBoxColor.argb) Then
			SetColor(_debugBoundingBoxColor.r, _debugBoundingBoxColor.g, _debugBoundingBoxColor.b)
			FlxG._lastDrawingColor = _debugBoundingBoxColor.argb
		End If

		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)

		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
			_separateXCallback.invoker = Self
			Return FlxTilemap(object1).OverlapsWithCallback(object2, _separateXCallback)
		End If

		If (FlxTilemap(object2) <> Null) Then
			_separateXCallback.invoker = Self
			Return FlxTilemap(object2).OverlapsWithCallback(object1, _separateXCallback, 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
			_separateYCallback.invoker = Self
			Return FlxTilemap(object1).OverlapsWithCallback(object2, _separateYCallback)
		End If

		If (FlxTilemap(object2) <> Null) Then
			_separateYCallback.invoker = Self
			Return FlxTilemap(object2).OverlapsWithCallback(object1, _separateYCallback, 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
				End If

			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

		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:Object()
		Return New FlxObject()
	End Method

	Method InstanceOf:Bool(object:Object)
		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 FlxObjectSeparateX Implements FlxTilemapOverlapListener

	Field invoker:FlxObject

	Method OnTilemapOverlap:Bool(object1:FlxObject, object2:FlxObject)
		invoker.SeparateX(object1, object2)
	End Method

End Class

Class FlxObjectSeparateY Implements FlxTilemapOverlapListener

	Field invoker:FlxObject

	Method OnTilemapOverlap:Bool(object1:FlxObject, object2:FlxObject)
		invoker.SeparateY(object1, object2)
	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


Animation Pause(), Play() and Restart() implemented

Strict

Private

Import directorevents
Import fader
Import fanout
Import transition

Public

Class Animation Extends FanOut
    Private

    Field startValue:Float
    Field endValue:Float
    Field duration:Float
    Field animationTime:Float
    Field finished:Bool
    Field _value:Float

    Public

    Field transition:Transition = New TransitionLinear()
    Field effect:Fader

    Method New(startValue:Float, endValue:Float, duration:Float)
        Self.startValue = startValue
        Self.endValue = endValue
        Self.duration = duration
    End

    Method Pause:Void()
        finished = True
    End

    Method Play:Void()
        finished = False
    End

    Method Restart:Void()
        animationTime = 0
        Play()
    End

    Method OnUpdate:Void(delta:Float, frameTime:Float)
        Super.OnUpdate(delta, frameTime)

        If finished Then Return
        animationTime += frameTime

        Local progress:Float = Min(1.0, animationTime / duration)
        Local t:Float = transition.Calculate(progress)
        _value = (startValue * (1.0 - t)) + (endValue * t)

        If animationTime >= duration
            animationTime = duration
            finished = True
        End
    End

    Method OnRender:Void()
        If Not effect
            Super.OnRender()
            Return
        End

        If Count() = 0 Then Return
        effect.PreRender(_value)

        For Local obj:DirectorEvents = EachIn Self
            effect.PreNode(_value, obj)
            obj.OnRender()
            effect.PostNode(_value, obj)
        End

        effect.PostRender(_value)
    End

    Method value:Float() Property
        Return _value
    End
End

Strict

Private

Import directorevents
Import fader
Import fanout
Import transition

Public

Class Animation Extends FanOut
    Private

    Field startValue:Float
    Field endValue:Float
    Field duration:Float
    Field animationTime:Float
    Field finished:Bool
    Field _value:Float

    Public

    Field transition:Transition = New TransitionLinear()
    Field effect:Fader

    Method New(startValue:Float, endValue:Float, duration:Float)
        Self.startValue = startValue
        Self.endValue = endValue
        Self.duration = duration
    End

    Method OnUpdate:Void(delta:Float, frameTime:Float)
        Super.OnUpdate(delta, frameTime)

        If finished Then Return
        animationTime += frameTime

        Local progress:Float = Min(1.0, animationTime / duration)
        Local t:Float = transition.Calculate(progress)
        _value = (startValue * (1.0 - t)) + (endValue * t)

        If animationTime >= duration
            animationTime = duration
            finished = True
        End
    End

    Method OnRender:Void()
        If Not effect
            Super.OnRender()
            Return
        End

        If Count() = 0 Then Return
        effect.PreRender(_value)

        For Local obj:DirectorEvents = EachIn Self
            effect.PreNode(_value, obj)
            obj.OnRender()
            effect.PostNode(_value, obj)
        End

        effect.PostRender(_value)
    End

    Method value:Float() Property
        Return _value
    End
End


odd error check in ClearScene removed

odd error check in ClearScene removed

Strict

Private

Import bono
Import mojo.graphics

Public

Class ClearScene Implements Renderable, Colorable
    Private

    Field color:Color

    Public

    Method New(color:Color=New Color())
        SetColor(color)
    End

    Method OnRender:Void()
        Cls(color.red, color.green, color.blue)
    End

    Method SetColor:Void(color:Color)
        Self.color = color
    End

    Method GetColor:Color()
        Return color
    End
End

Strict

Private

Import bono
Import mojo.graphics

Public

Class ClearScene Implements Renderable, Colorable
    Private

    Field color:Color

    Public

    Method New(color:Color=New Color())
        SetColor(color)
    End

    Method OnRender:Void()
        If Not color Then Error("This should nerver happen")
        Cls(color.red, color.green, color.blue)
    End

    Method SetColor:Void(color:Color)
        Self.color = color
    End

    Method GetColor:Color()
        Return color
    End
End


virtual resolution fixed

virtual resolution fixed

Strict

Import mojo

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

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _fps:Int

	Field _fpsCounter:Int

	Field _lastFpsCounter:Int

	Field _fpsTime:Int

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

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

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

		_state = Null

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

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

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

	Method OnCreate:Int()
		SetUpdateRate(FlxG.framerate)
		Seed = SystemMillisecs()

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

		If (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

		_InitData()
		_step = 1000.0 / FlxG.framerate
		_fps = -1
		_Step()

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

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

					If (FlxG.volumeHandler <> Null) Then
						If (FlxG.mute) Then
							FlxG.volumeHandler.OnVolumeChange(0)
						Else
							FlxG.volumeHandler.OnVolumeChange(FlxG.Volume())
						End If
					End If

					_ShowSoundTray()
				End If

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

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

		Return 0
	End Method

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

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

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

		_UpdateSoundTray()
		_Step()

		Cls(FlxG._bgColor.r, FlxG._bgColor.g, FlxG._bgColor.b)
		Scale(FlxG._deviceScaleFactorX, FlxG._deviceScaleFactorY)

		FlxG._lastDrawingColor = FlxG.WHITE
		FlxG._lastDrawingBlend = GetBlend()
		FlxG._lastDrawingAlpha = GetAlpha()
		FlxG._currentCamera = Null

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

		While(i < l)
			FlxG._currentCamera = FlxG.cameras.Get(i)

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

			FlxG._currentCameraID = i

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

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

			i+=1
		Wend

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

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

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

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

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

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

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

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

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.mouse.Draw()

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

		Return 0
	End Method

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

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

	Method OnContentInit:Void()
	End Method

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

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()

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

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

		_state = _requestedState
		_state.Create()
	End Method

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

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

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

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

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

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ACTIVECOUNT = 0

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

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

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

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

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

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

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

		If (_recording) Then
			_replay.RecordFrame()

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

		_Update()

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

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

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

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (FlxG.elapsed * FlxG.height * 2)

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

				'TODO Save sound settings
			End If
		End If
	End Method

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

	Method _InitData:Void()
		FlxAssetsManager.Init()

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

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

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

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

		Self.OnContentInit()
	End Method
End Class

Strict

Import mojo

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

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _fps:Int

	Field _fpsCounter:Int

	Field _lastFpsCounter:Int

	Field _fpsTime:Int

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

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

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

		_state = Null

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

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

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

	Method OnCreate:Int()
		SetUpdateRate(FlxG.framerate)
		Seed = SystemMillisecs()

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

		If (useVirtualResolution) Then
			FlxG._deviceScaleFactorX = FlxG.deviceWidth / FlxG.width
			FlxG._deviceScaleFactorY = FlxG.deviceHeight / FlxG.height
		Else
			FlxG._deviceScaleFactorX = 1
			FlxG._deviceScaleFactorY = 1
		End If

		_InitData()
		_step = 1000.0 / FlxG.framerate
		_fps = -1
		_Step()

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

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

					If (FlxG.volumeHandler <> Null) Then
						If (FlxG.mute) Then
							FlxG.volumeHandler.OnVolumeChange(0)
						Else
							FlxG.volumeHandler.OnVolumeChange(FlxG.Volume())
						End If
					End If

					_ShowSoundTray()
				End If

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

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

		Return 0
	End Method

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

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

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

		_UpdateSoundTray()
		_Step()

		Cls(FlxG._bgColor.r, FlxG._bgColor.g, FlxG._bgColor.b)
		Scale(FlxG._deviceScaleFactorX, FlxG._deviceScaleFactorY)

		FlxG._lastDrawingColor = FlxG.WHITE
		FlxG._lastDrawingBlend = GetBlend()
		FlxG._lastDrawingAlpha = GetAlpha()
		FlxG._currentCamera = Null

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

		While(i < l)
			FlxG._currentCamera = FlxG.cameras.Get(i)

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

			FlxG._currentCameraID = i

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

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

			i+=1
		Wend

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

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

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

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

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

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

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

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

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.mouse.Draw()

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

		Return 0
	End Method

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

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

	Method OnContentInit:Void()
	End Method

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

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()

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

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

		_state = _requestedState
		_state.Create()
	End Method

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

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

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

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

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

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ACTIVECOUNT = 0

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

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

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

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

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

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

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

		If (_recording) Then
			_replay.RecordFrame()

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

		_Update()

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

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

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

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (FlxG.elapsed * FlxG.height * 2)

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

				'TODO Save sound settings
			End If
		End If
	End Method

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

	Method _InitData:Void()
		FlxAssetsManager.Init()

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

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

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

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

		Self.OnContentInit()
	End Method
End Class


Highscore sort last added first on same score

Strict

Private

Import persistable
Import score

Public

Class Highscore<T> Implements Persistable
    Private

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

    Public

    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 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

    Method First:Score<T>()
        If objects.Count() = 0 Then Return New Score<T>("", 0)
        Return objects.First()
    End

    Method Last:Score<T>()
        If objects.Count() = 0 Then Return New Score<T>("", 0)
        Return objects.Last()
    End

    Method maxCount:Int() Property
        Return _maxCount
    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 persistable
Import score

Public

Class Highscore<T> Implements Persistable
    Private

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

    Public

    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 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

    Method First:Score<T>()
        If objects.Count() = 0 Then Return New Score<T>("", 0)
        Return objects.First()
    End

    Method Last:Score<T>()
        If objects.Count() = 0 Then Return New Score<T>("", 0)
        Return objects.Last()
    End

    Method maxCount:Int() Property
        Return _maxCount
    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


DisplayObject fields should be private

DisplayObject fields should be private

Strict

Private

Import renderable
Import vector2d

Public

Class DisplayObject Implements Renderable Abstract
    Private

    Field _pos:Vector2D
    Field _center:Vector2D
    Field _size:Vector2D

    Public

    Method pos:Vector2D() Property
        If _pos = Null Then Error("Position not set yet.")
        Return _pos
    End

    Method pos:Void(newPos:Vector2D) Property
        _pos = newPos
    End

    Method size:Vector2D() Property
        If _size = Null Then Error("Size not set yet.")
        Return _size
    End

    Method size:Void(newSize:Vector2D) Property
        _size = newSize
        _center = newSize.Copy().Div(2)
    End

    Method center:Vector2D() Property
        If _center = Null Then Error("No size set and center therefore unset.")
        Return _center
    End

    Method OnRender:Void() Abstract
    Method OnUpdate:Void() Abstract
End

Strict

Private

Import renderable
Import vector2d

Public

Class DisplayObject Implements Renderable Abstract
    Field _pos:Vector2D
    Field _center:Vector2D
    Field _size:Vector2D

    Method pos:Vector2D() Property
        If _pos = Null Then Error("Position not set yet.")
        Return _pos
    End

    Method pos:Void(newPos:Vector2D) Property
        _pos = newPos
    End

    Method size:Vector2D() Property
        If _size = Null Then Error("Size not set yet.")
        Return _size
    End

    Method size:Void(newSize:Vector2D) Property
        _size = newSize
        _center = newSize.Copy().Div(2)
    End

    Method center:Vector2D() Property
        If _center = Null Then Error("No size set and center therefore unset.")
        Return _center
    End

    Method OnRender:Void() Abstract
    Method OnUpdate:Void() Abstract
End