FLX_WEBGL_ENABLED false by default #72

FLX_WEBGL_ENABLED false by default #72


Tilemap loading bug fixed #49

Tilemap loading bug fixed #49

Strict

Import mojo
Import reflection

Import flxextern
Import flxobject
Import flxrect
Import flxg
Import flxcamera
Import flxgroup
Import flxbasic
Import flxpath

Import system.flxtile
Import system.flxtilemapbuffer

Import "data/autotiles_flx.png"
Import "data/autotiles_flx.png"

Class FlxTilemap Extends FlxObject

	Global ClassObject:Object

	Const AUTOTILES:String =  "autotiles" + FlxG.DATA_SUFFIX

	Const AUTOTILES_ALT:String =  "autotiles_alt" + FlxG.DATA_SUFFIX

	Const OFF:Int = 0

	Const AUTO:Int = 1

	Const ALT:Int = 2

	Field auto:Int

	Field widthInTiles:Int

	Field heightInTiles:Int

	Field totalTiles:Int

Private
	Global _TileLoader:FlxTileLoader = New FlxTileLoader()

	Field _tiles:Image

	Field _buffers:IntMap<FlxTilemapBuffer>

	Field _data:Int[]

	Field _rects:FlxRect[]

	Field _tileWidth:Int

	Field _tileHeight:Int

	Field _tileObjects:FlxTile[]

	Field _startingIndex:Int = 0

	Field _camera:FlxCamera

	Field _buffer:FlxTilemapBuffer

	Field _screenXInTiles:Int

	Field _screenYInTiles:Int

	Field _screenRows:Int

	Field _screenColumns:Int

Public
	Method New()
		Super.New()
		auto = OFF
		widthInTiles = 0
		heightInTiles = 0
		totalTiles = 0
		_buffers = New IntMap<FlxTilemapBuffer>()
		_tileWidth = 0
		_tileHeight = 0
		_tiles = Null
		immovable = True
		_cameras = Null
		_startingIndex = 0
		_camera = Null
		_buffer = Null
	End Method

	Method Destroy:Void()
		_tiles = Null
		_camera = Null
		_buffer = Null

		Local i:Int = 0
		Local l:Int = _tileObjects.Length()

		While (i < l)
			If (_tileObjects[i] <> Null) Then
				_tileObjects[i].Destroy()
				_tileObjects[i] = Null
			End If

			i += 1
		Wend

		_buffers.Clear()
		_buffers = Null

		Super.Destroy()
	End Method

	Method LoadMap:FlxTilemap(mapData:String, tileGraphic:String, tileWidth:Int = 0, tileHeight:Int = 0, autoTile:Int = OFF, startIndex:Int = 0, drawIndex:Int = 1, collideIndex:Int = 1)
		auto = autoTile
		_startingIndex = startIndex

		Local columns:String[]
		Local rows:String[] = mapData.Split("~n")
		Local row:Int = 0
		Local column:Int
		Local data:Stack<Int> = New Stack<Int>()

		heightInTiles = rows.Length()

		While (row < heightInTiles)
			columns = rows[row].Split(",")

			If (columns.Length() <= 1) Then
				heightInTiles = heightInTiles - 1
				Continue
			End If

			If (widthInTiles = 0) Then
				widthInTiles = columns.Length()
			End If

			column = 0

			While (column < widthInTiles)
				data.Push(Int(columns[column]))
				column += 1
			Wend

			row += 1
		Wend

		_data = data.ToArray()

		Local i:Int

		totalTiles = widthInTiles * heightInTiles

		If (auto > OFF) Then
			_startingIndex = 1
			drawIndex = 1
			collideIndex = 1
			i = 0

			While (i < totalTiles)
				_AutoTile(i)
				i += 1
			Wend
		End If

		_tiles = FlxG.AddBitmap(tileGraphic, _TileLoader)

		_tileWidth = tileWidth
		If (_tileWidth = 0) _tileWidth = _tiles.Height()

		_tileHeight = tileHeight
		If (_tileHeight = 0) _tileHeight = _tileWidth

		i = 0
		Local l:Int = (_tiles.Width() / _tileWidth) * (_tiles.Height() / _tileHeight)

		If (auto > OFF) l += 1
		_tileObjects = _tileObjects.Resize(l)

		While (i < l)
			If (i >= collideIndex) Then
				_tileObjects[i] = New FlxTile(Self, i, _tileWidth, _tileHeight, (i >= drawIndex), allowCollisions)
			Else
				_tileObjects[i] = New FlxTile(Self, i, _tileWidth, _tileHeight, (i >= drawIndex), NONE)
			End If

			i += 1
		Wend

		width = widthInTiles * _tileWidth
		height = heightInTiles * _tileHeight
		_rects = _rects.Resize(totalTiles)
		i = 0

		While (i < totalTiles)
			_UpdateTile(i)
			i += 1
		Wend

		_buffers.Clear()

		Return Self
	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.ID)) Return

		_buffer = _buffers.Get(_camera.ID)

		If (_buffer = Null) Then
			_buffer = New FlxTilemapBuffer(_tileWidth, _tileHeight, widthInTiles, heightInTiles, _camera)
			_buffers.Set(_camera.ID, _buffer)
		End If

		_screenRows = _buffer.rows
		_screenColumns = _buffer.columns

		If (Not _buffer.dirty) Then
			_point.x = x - Int(_camera.scroll.x * scrollFactor.x) + _buffer.x
			_point.y = y - Int(_camera.scroll.y * scrollFactor.y) + _buffer.y

			_buffer.dirty = _point.x > 0 Or _point.y > 0 Or _point.x + _buffer.width < _camera.Width Or _point.y + _buffer.height < _camera.Height
		End If

		If (_buffer.dirty) Then
			_point.x = Int(_camera.scroll.x * scrollFactor.x) - x
			_point.y = Int(_camera.scroll.y * scrollFactor.y) - y

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

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

			If (_screenXInTiles < 0) _screenXInTiles = 0
			If (_screenXInTiles > widthInTiles - _screenColumns) _screenXInTiles = widthInTiles - _screenColumns
			If (_screenYInTiles < 0) _screenYInTiles = 0
			If (_screenYInTiles > heightInTiles - _screenRows) _screenYInTiles = heightInTiles - _screenRows

			_buffer.x = _screenXInTiles * _tileWidth
			_buffer.y = _screenYInTiles * _tileHeight
		End If

		_point.x = x - Int(_camera.scroll.x * scrollFactor.x) + _buffer.x
		_point.y = y - Int(_camera.scroll.y * scrollFactor.y) + _buffer.y

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

		If (FlxG._LastDrawingColor <> _camera.Color) Then
			SetColor(_camera._color.r, _camera._color.g, _camera._color.b)
			FlxG._LastDrawingColor = _camera.Color
		End If

		Local alpha:Float = _camera._color.a * _camera.Alpha

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

		PushMatrix()
			Translate( _point.x, _point.y)

			Local rowIndex:Int = _screenYInTiles * widthInTiles + _screenXInTiles
			Local row:Int = 0
			Local column:Int
			Local columnIndex:Int

			_point.y = 0

			If (_buffer.scaleFixX = 1 Or _buffer.scaleFixY = 1) Then
				While (row < _screenRows)
					columnIndex = rowIndex
					column = 0
					_point.x = 0

					While (column < _screenColumns)
						_rect = _rects[columnIndex]

						If (_rect <> Null) Then
							DrawImageRect(_tiles, _point.x, _point.y, _rect.x, _rect.y, _rect.width, _rect.height)

							If (FlxG.VisualDebug And Not ignoreDrawDebug) Then
								'TODO
							End If
						End If

						_point.x += _tileWidth
						column += 1
						columnIndex += 1
					Wend

					rowIndex += widthInTiles
					_point.y += _tileHeight
					row += 1
				Wend
			Else
				row = _screenRows - 1
				_point.y = row * _tileHeight
				rowIndex += widthInTiles * row

				While (row >= 0)
					column = _screenColumns - 1
					_point.x = column * _tileWidth
					columnIndex = rowIndex + column

					While (column >= 0)
						_rect = _rects[columnIndex]

						If (_rect <> Null) Then
							DrawImageRect(_tiles, _point.x, _point.y, _rect.x, _rect.y, _rect.width, _rect.height, 0, _buffer.scaleFixX, _buffer.scaleFixY)

							If (FlxG.VisualDebug And Not ignoreDrawDebug) Then
								'TODO
							End If
						End If

						_point.x -= _tileWidth
						column -= 1
						columnIndex -= 1
					Wend

					rowIndex -= widthInTiles
					_point.y -= _tileHeight
					row -= 1
				Wend
			End If
		PopMatrix()

		_VisibleCount += 1
	End Method

	Method GetData:Int[](simple:Bool = False)
		If (Not simple) Return _data

		Local i:Int = 0
		Local l:Int = _data.Length()
		Local data:Int[l]

		While (i < l)
			If (_tileObjects[_data[i]].allowCollisions > 0) Then
				data[i] = 1
			Else
				data[i] = 0
			End If

			i += 1
		Wend

		Return data
	End Method

	Method SetDirty:Void(dirty:Bool = True)
		For Local buffer:FlxTilemapBuffer = EachIn _buffers.Values()
			buffer.dirty = dirty
		Next
	End Method

	Method FindPath:FlxPath(start:FlxPoint, endPoint:FlxPoint, simplify:Bool = True, raySimplify:Bool = False)
		Local startIndex:Int = Int((start.y - y) / _tileHeight) * widthInTiles + Int((start.x - x) / _tileWidth)
		Local endIndex:Int = Int((endPoint.y - y) / _tileHeight) * widthInTiles + Int((endPoint.x - x) / _tileWidth)

		If (_tileObjects[_data[startIndex]].allowCollisions > 0 Or _tileObjects[_data[endIndex]].allowCollisions > 0) Then
			Return Null
		End If

		Local distances:Int[] = _ComputePathDistance(startIndex, endIndex)
		If (distances.Length() = 0) Return Null

		Local points:Stack<FlxPoint> = New Stack<FlxPoint>()
		_WalkPath(distances, endIndex, points)

		Local node:FlxPoint

		node = points.Top()
		node.x = start.x
		node.y = start.y

		node = points.Get(0)
		node.x = endPoint.x
		node.y = endPoint.y

		If (simplify) _SimplifyPath(points)
		If (raySimplify) _RaySimplifyPath(points)

		Local path:FlxPath = New FlxPath()
		Local i:Int = points.Length() - 1

		While (i >= 0)
			node = points.Get(i)

			If (node <> Null) Then
				path.AddPoint(node, true)
			End If

			i -= 1
		Wend

		Return path
	End Method

	Method Overlaps:Bool(objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local basic:FlxBasic
			Local i:Int = 0
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local l:Int = members.Length()

			While (i < l)
				basic = members[i]

				If (FlxObject(basic) <> Null) Then
					If (OverlapsWithCallback(FlxObject(basic))) Then
						results = True
					End If

				Else
					If (Overlaps(basic, inScreenSpace, camera)) Then
						results = True
					End If
				End If

				i += 1
			Wend

			Return results

		ElseIf (FlxObject(objectOrGroup) <> Null) Then
			Return OverlapsWithCallback(FlxObject(objectOrGroup))
		End If

		Return False
	End Method

	Method OverlapsAt:Bool(x:Float, y:Float, objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local basic:FlxBasic
			Local i:Int = 0
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local l:Int = members.Length()

			While (i < l)
				basic = members[i]

				If (FlxObject(basic) <> Null) Then
					_point.x = x
					_point.y = y

					If (OverlapsWithCallback(FlxObject(basic), Null, False, _point)) Then
						results = True
					End If

				Else
					If (OverlapsAt(x, y, basic, inScreenSpace, camera)) Then
						results = True
					End If
				End If

				i += 1
			Wend

			Return results

		ElseIf (FlxObject(objectOrGroup) <> Null) Then
			_point.x = x
			_point.y = y

			Return OverlapsWithCallback(FlxObject(objectOrGroup), Null, False, _point)
		End If

		Return False
	End Method

	Method OverlapsWithCallback:Bool(object:FlxObject, callback:FlxTileOverlapChecker = Null, flipCallbackParams:Bool = False, position:FlxPoint = Null)
		Local results:Bool = False

		Local lx:Float = x
		Local ly:Float = y

		If (position <> Null) Then
			lx = position.x
			ly = position.y
		End If

		Local selectionX:Int = Floor((object.x - lx) / _tileWidth)
		Local selectionY:Int = Floor((object.y - ly) / _tileHeight)
		Local selectionWidth:Int = selectionX + Ceil(object.width / _tileWidth) + 1
		Local selectionHeight:Int = selectionY + Ceil(object.height / _tileHeight) + 1

		If (selectionX < 0) selectionX = 0
		If (selectionY < 0) selectionY = 0
		If (selectionWidth > widthInTiles) selectionWidth = widthInTiles
		If (selectionHeight > heightInTiles) selectionHeight = heightInTiles

		Local rowStart:Int = selectionY * widthInTiles
		Local row:Int = selectionY
		Local column:Int
		Local tile:FlxTile
		Local overlapFound:Bool
		Local deltaX:Float = lx - last.x
		Local deltaY:Float = ly - last.y

		While (row < selectionHeight)
			column = selectionX

			While (column < selectionWidth)
				overlapFound = False
				tile = _tileObjects[_data[rowStart + column]]

				If (tile.allowCollisions) Then
					tile.x = lx + column * _tileWidth
					tile.y = ly + row * _tileHeight
					tile.last.x = tile.x - deltaX
					tile.last.y = tile.y - deltaY

					If (callback <> Null) Then
						If (flipCallbackParams) Then
							overlapFound = callback.IsTileOverlap(object, tile)
						Else
							overlapFound = callback.IsTileOverlap(tile, object)
						End If
					Else
						overlapFound = (object.x + object.width > tile.x And object.x < tile.x + tile.width And object.y + object.height > tile.y And object.y < tile.y + tile.height)
					End If

					If (overlapFound) Then
						If (tile.callback <> Null And (tile.filter = Null Or object.GetClass().ExtendsClass(tile.filter))) Then
							tile.mapIndex = rowStart + column
							tile.callback.OnTileHit(tile, object)
						End If

						results = True
					End If

				ElseIf (tile.callback <> Null And (tile.filter = Null Or object.GetClass().ExtendsClass(tile.filter))) Then
					tile.mapIndex = rowStart + column
					tile.callback.OnTileHit(tile, object)
				End If

				column += 1
			Wend

			rowStart += widthInTiles
			row += 1
		Wend

		Return results
	End Method

	Method OverlapsPoint:Bool(point:FlxPoint, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (Not inScreenSpace) Then
			Return _tileObjects[_data[Int(Int((point.y - y) / _tileHeight) * widthInTiles + (point.x - x) / _tileWidth)]].allowCollisions > 0
		End If

		If (camera = Null) camera = FlxG.Camera

		point.x -= camera.scroll.x
		point.y -= camera.scroll.y

		GetScreenXY(_point, camera)

		Return _tileObjects[_data[Int(Int((point.y - _point.y) / _tileHeight) * widthInTiles + (point.x - _point.x) / _tileWidth)]].allowCollisions > 0
	End Method

	Method GetTile:Int(x:Int, y:Int)
		Return _data[y * widthInTiles + x]
	End Method

	Method GetTileByIndex:Int(index:Int)
		Return _data[index]
	End Method

	Method GetTileInstances:Stack<Int>(index:Int)
		Local elements:Stack<Int> = Null

		Local i:Int = 0
		Local l:Int = widthInTiles * heightInTiles

		While (i < l)
			If (_data[i] = index) Then
				If (elements = Null) elements = New Stack<Int>()
				elements.Push(i)
			End If

			i += 1
		Wend

		Return elements
	End Method

	Method GetTileCoords:Stack<FlxPoint>(index:Int, midpoint:Bool = True)
		Local elements:Stack<FlxPoint> = Null

		Local point:FlxPoint
		Local i:Int = 0
		Local l:Int = widthInTiles * heightInTiles

		While (i < l)
			If (_data[i] = index) Then
				point = New FlxPoint(x + Int(i Mod widthInTiles) * _tileWidth, y + Int(i / widthInTiles) * _tileHeight)

				If (midpoint) Then
					point.x += _tileWidth * .5
					point.y += _tileHeight * .5
				End If

				If (elements = Null) elements = New Stack<FlxPoint>()
				elements.Push(point)
			End If

			i += 1
		Wend

		Return elements
	End Method

	Method SetTile:Bool(x:Int, y:Int, tile:Int, updateGraphics:Bool = True)
		If (x >= widthInTiles Or y >= heightInTiles) Then
			Return False
		End If

		Return SetTileByIndex(y * widthInTiles + x, tile, updateGraphics)
	End Method

	Method SetTileByIndex:Bool(index:Int, tile:Int, updateGraphics:Bool = True)
		If (index >= _data.Length()) Return False

		_data[index] = tile

		If (Not updateGraphics) Return True

		If (auto = OFF) Then
			_UpdateTile(index)
			Return True
		End If

		Local i:Int = 0
		Local row:Int = Int(index / widthInTiles) - 1
		Local rowLength:Int = row + 3
		Local column:Int = index Mod widthInTiles - 1
		Local columnHeight:Int = column + 3

		While (row < rowLength)
			column = columnHeight - 3

			While (column < columnHeight)
				If (row >= 0 And row < heightInTiles And column >= 0 And column < widthInTiles) Then
					i = row * widthInTiles + column
					_AutoTile(i)
					_UpdateTile(i)
				End If

				column += 1
			Wend

			row += 1
		Wend

		Return True
	End Method

	Method SetTileProperties:Void(tile:Int, allowCollisions:Int = $1111, callback:FlxTileHitListener = Null, callbackFilter:ClassInfo = Null, range:Int = 1)
		If (range <= 0) range = 1

		Local tileObject:FlxTile
		Local i:Int = tile
		Local l:Int = tile + range

		While (i < l)
			tileObject = _tileObjects[i]

			tileObject.allowCollisions = allowCollisions
			tileObject.callback = callback
			tileObject.filter = callbackFilter

			i += 1
		Wend
	End Method

	Method Follow:Void(camera:FlxCamera = Null, border:Int = 0, updateWorld:Bool = True)
		If (camera = Null) camera = FlxG.Camera
		camera.SetBounds(x + border * _tileWidth, y + border * _tileHeight, width - border * _tileWidth * 2, height - border * _tileHeight * 2, updateWorld)
	End Method

	Method GetBounds:FlxRect(bounds:FlxRect = Null)
		If (bounds = Null) bounds = New FlxRect()
		Return bounds.Make(x, y, width, height)
	End Method

	Method Ray:Bool(startPoint:FlxPoint, endPoint:FlxPoint, result:FlxPoint = Null, resolution:Float = 1)
		Local _step:Float = _tileWidth

		If (_tileHeight < _tileWidth) Then
			_step = _tileHeight
		End If

		_step /= resolution

		Local deltaX:Float = endPoint.x - startPoint.x
		Local deltaY:Float = endPoint.y - startPoint.y
		Local distance:Float = Sqrt(deltaX * deltaX + deltaY * deltaY)
		Local steps:Int = Ceil(distance / _step)
		Local stepX:Float = deltaX / steps
		Local stepY:Float = deltaY / steps
		Local curX:Float = startPoint.x - stepX - x
		Local curY:Float = startPoint.y - stepY - y
		Local tileX:Int
		Local tileY:Int
		Local i:Int = 0

		While (i < steps)
			curX += stepX
			curY += stepY

			If (curX < 0 Or curX > width Or curY < 0 Or curY > height) Then
				i += 1
				Continue
			End If

			tileX = curX / _tileWidth
			tileY = curY / _tileHeight

			If (_tileObjects[_data[tileY * widthInTiles + tileX]].allowCollisions) Then
				tileX *= _tileWidth
				tileY *= _tileHeight

				Local rx:Float = 0
				Local ry:Float = 0
				Local q:Float
				Local lx:Float = curX - stepX
				Local ly:Float = curY - stepY

				q = tileX
				If (deltaX < 0) q += _tileWidth

				rx = q
				ry = ly + stepY * ((q - lx) / stepX)

				If (ry > tileY And ry < tileY + _tileHeight) Then
					If (result = Null) Then
						result = New FlxPoint()
					End If

					result.x = rx
					result.y = ry

					Return False
				End If

				q = tileY
				If (deltaY < 0) q += _tileHeight

				rx = lx + stepX * ((q - ly) / stepY)
				ry = q

				If (rx > tileX And rx < tileX + _tileWidth) Then
					If (result = Null) Then
						result = New FlxPoint()
					End If

					result.x = rx
					result.y = ry

					Return False
				End If

				Return True
			End If

			i += 1
		Wend

		Return True
	End Method

	Function ArrayToCSV:String(data:Int[], width:Int, invert:Bool = False)
		Local row:Int = 0
		Local column:Int
		Local csv:StringStack = New StringStack()
		Local height:Int = data.Length() / width
		Local index:Int

		While (row < height)
			column = 0

			While (column < width)
				index = data[row * width + column]

				If (invert) Then
					If (index = 0) Then
						index = 1
					Else
						index = 0
					End If
				End If

				If (column = 0) Then
					If (row = 0) Then
						csv.Push(index)
					Else
						csv.Push("~n" + index)
					End If
				Else
					csv.Push(", " + index)
				End If

				column += 1
			Wend

			row += 1
		Wend

		Return csv.Join("")
	End Function

Private
	Method _SimplifyPath:Void(points:Stack<FlxPoint>)
		Local deltaPrevious:Float
		Local deltaNext:Float
		Local last:FlxPoint = points.Get(0)
		Local node:FlxPoint
		Local nextPoint:FlxPoint
		Local i:Int = 1
		Local l:Int = points.Length() - 1

		While (i < l)
			node = points.Get(i)
			nextPoint = points.Get(i + 1)

			deltaPrevious = (node.x - last.x) / (node.y - last.y)
			deltaNext = (node.x - nextPoint.x) / (node.y - nextPoint.y)

			If (last.x = nextPoint.x Or last.y = nextPoint.y Or deltaPrevious = deltaNext) Then
				points.Set(i, Null)
			Else
				last = node
			End If

			i += 1
		Wend
	End Method

	Method _RaySimplifyPath:Void(points:Stack<FlxPoint>)
		Local source:FlxPoint = points.Get(0)
		Local lastIndex:Int = -1
		Local node:FlxPoint
		Local i:Int = 1
		Local l:Int = points.Length()

		While (i < l)
			node = points.Get(i)
			i += 1

			If (node = Null) Continue

			If (Ray(source, node, _point)) Then
				If (lastIndex >= 0) points.Set(lastIndex, Null)
			Else
				source = points.Get(lastIndex)
			End If

			lastIndex = i - 1
		Wend
	End Method

	Method _ComputePathDistance:Int[](startIndex:Int, endIndex:Int)
		Local mapSize:Int = widthInTiles * heightInTiles
		Local distances:Int[mapSize]
		Local i:Int = 0

		While (i < mapSize)
			If (_tileObjects[_data[i]].allowCollisions) Then
				distances[i] = -2
			Else
				distances[i] = -1
			End If

			i += 1
		Wend

		distances[startIndex] = 0

		Local distance:Int = 1
		Local neighbors:Stack<Int> = New Stack<Int>([startIndex])
		Local current:Stack<Int>
		Local currentIndex:Int
		Local left:Bool
		Local right:Bool
		Local up:Bool
		Local down:Bool
		Local currentLength:Int
		Local foundEnd:Bool = False

		While (neighbors.Length() > 0)
			current = neighbors
			neighbors = New Stack<Int>()

			i = 0
			currentLength = current.Length()

			While (i < currentLength)
				currentIndex = current.Get(i)

				If (currentIndex = endIndex) Then
					foundEnd = True
					neighbors.Clear()
					Exit
				End If

				left = currentIndex Mod widthInTiles > 0
				right = currentIndex Mod widthInTiles < widthInTiles - 1
				up = currentIndex / widthInTiles > 0
				down = currentIndex / widthInTiles < heightInTiles - 1

				Local index:Int

				If (up) Then
					index = currentIndex - widthInTiles

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (right) Then
					index = currentIndex + 1

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (down) Then
					index = currentIndex + widthInTiles

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (left) Then
					index = currentIndex - 1

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (up And right) Then
					index = currentIndex - widthInTiles + 1

					If (distances[index] = -1 And distances[currentIndex - widthInTiles] >= -1 And distances[currentIndex + 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (right And down) Then
					index = currentIndex + widthInTiles + 1

					If (distances[index] = -1 And distances[currentIndex + widthInTiles] >= -1 And distances[currentIndex + 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (left And down) Then
					index = currentIndex + widthInTiles - 1

					If (distances[index] = -1 And distances[currentIndex + widthInTiles] >= -1 And distances[currentIndex - 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (up And left) Then
					index = currentIndex - widthInTiles - 1

					If (distances[index] = -1 And distances[currentIndex - widthInTiles] >= -1 And distances[currentIndex - 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				i += 1
			Wend

			distance += 1
		Wend

		If (Not foundEnd) distances = []

		Return distances
	End Method

	Method _WalkPath:Void(data:Int[], start:Int, points:Stack<FlxPoint>)
		points.Push(New FlxPoint(x + Int(start Mod widthInTiles) * _tileWidth + _tileWidth * .5, y + Int(start / widthInTiles) * _tileHeight + _tileHeight * .5))
		If (data[start] = 0) Return

		Local left:Bool = start Mod widthInTiles > 0
		Local right:Bool = start Mod widthInTiles < widthInTiles - 1
		Local up:Bool = start / widthInTiles > 0
		Local down:Bool = start / widthInTiles < heightInTiles - 1

		Local current:Int = data[start]
		Local i:Int

		If (up) Then
			i = start - widthInTiles

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (right) Then
			i = start + 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (down) Then
			i = start + widthInTiles

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (left) Then
			i = start - 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (up And right) Then
			i = start - widthInTiles + 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (right And down) Then
			i = start + widthInTiles + 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (left And down) Then
			i = start + widthInTiles - 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (up And left) Then
			i = start - widthInTiles - 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If
	End Method

	Method _AutoTile:Void(index:Int)
		If (_data[index] = 0) Return

		_data[index] = 0

		If ( (index - widthInTiles < 0) Or (_data[index - widthInTiles] > 0)) Then
			_data[index] += 1
		End If

		If ( (index Mod widthInTiles >= widthInTiles - 1) Or (_data[index + 1] > 0)) Then
			_data[index] += 2
		End If

		If ( (index + widthInTiles >= totalTiles) Or (_data[index + widthInTiles] > 0)) Then
			_data[index] += 4
		End If

		If ( (index Mod widthInTiles <= 0) Or (_data[index - 1] > 0)) Then
			_data[index] += 8
		End If

		If (auto = ALT And _data[index] = 15) Then
			If ( (index Mod widthInTiles > 0) And (index + widthInTiles < totalTiles) And (_data[index + widthInTiles - 1] <= 0)) Then
				_data[index] = 1
			End If

			If ( (index Mod widthInTiles > 0) And (index - widthInTiles >= 0) And (_data[index - widthInTiles - 1] <= 0)) Then
				_data[index] = 2
			End If

			If ( (index Mod widthInTiles < widthInTiles - 1) And (index - widthInTiles >= 0) And (_data[index - widthInTiles + 1] <= 0)) Then
				_data[index] = 4
			End If

			If ( (index Mod widthInTiles < widthInTiles - 1) And (index + widthInTiles < totalTiles) And (_data[index + widthInTiles + 1] <= 0)) Then
				_data[index] = 8
			End If
		End If

		_data[index] += 1
	End Method

	Method _UpdateTile:Void(index:Int)
		Local tile:FlxTile = _tileObjects[_data[index]]

		If (tile = Null Or Not tile.visible) Then
			_rects[index] = Null
			Return
		End If

		Local rx:Int = (_data[index] - _startingIndex) * _tileWidth
		Local ry:Int = 0

		If (rx >= _tiles.Width()) Then
			ry = Int(rx / _tiles.Width()) * _tileHeight
			rx Mod= _tiles.Width()
		End If

		_rects[index] = New FlxRect(rx, ry, _tileWidth, _tileHeight)
	End Method

End Class

Private
Class FlxTileLoader Extends FlxResourceLoader<Image>

	Method Load:Image(name:String)
		Return LoadImage(FlxAssetsManager.GetImagePath(name))
	End Method

End Class

Strict

Import mojo
Import reflection

Import flxextern
Import flxobject
Import flxrect
Import flxg
Import flxcamera
Import flxgroup
Import flxbasic
Import flxpath

Import system.flxtile
Import system.flxtilemapbuffer

Import "data/autotiles_flx.png"
Import "data/autotiles_flx.png"

Class FlxTilemap Extends FlxObject

	Global ClassObject:Object

	Const AUTOTILES:String =  "autotiles" + FlxG.DATA_SUFFIX

	Const AUTOTILES_ALT:String =  "autotiles_alt" + FlxG.DATA_SUFFIX

	Const OFF:Int = 0

	Const AUTO:Int = 1

	Const ALT:Int = 2

	Field auto:Int

	Field widthInTiles:Int

	Field heightInTiles:Int

	Field totalTiles:Int

Private
	Global _TileLoader:FlxTileLoader = New FlxTileLoader()

	Field _tiles:Image

	Field _buffers:IntMap<FlxTilemapBuffer>

	Field _data:Int[]

	Field _rects:FlxRect[]

	Field _tileWidth:Int

	Field _tileHeight:Int

	Field _tileObjects:FlxTile[]

	Field _startingIndex:Int = 0

	Field _camera:FlxCamera

	Field _buffer:FlxTilemapBuffer

	Field _screenXInTiles:Int

	Field _screenYInTiles:Int

	Field _screenRows:Int

	Field _screenColumns:Int

Public
	Method New()
		Super.New()
		auto = OFF
		widthInTiles = 0
		heightInTiles = 0
		totalTiles = 0
		_buffers = New IntMap<FlxTilemapBuffer>()
		_tileWidth = 0
		_tileHeight = 0
		_tiles = Null
		immovable = True
		_cameras = Null
		_startingIndex = 0
		_camera = Null
		_buffer = Null
	End Method

	Method Destroy:Void()
		_tiles = Null
		_camera = Null
		_buffer = Null

		Local i:Int = 0
		Local l:Int = _tileObjects.Length()

		While (i < l)
			If (_tileObjects[i] <> Null) Then
				_tileObjects[i].Destroy()
				_tileObjects[i] = Null
			End If

			i += 1
		Wend

		_buffers.Clear()
		_buffers = Null

		Super.Destroy()
	End Method

	Method LoadMap:FlxTilemap(mapData:String, tileGraphic:String, tileWidth:Int = 0, tileHeight:Int = 0, autoTile:Int = OFF, startIndex:Int = 0, drawIndex:Int = 1, collideIndex:Int = 1)
		auto = autoTile
		_startingIndex = startIndex

		Local columns:String[]
		Local rows:String[] = mapData.Split("~n")
		Local row:Int = 0
		Local column:Int
		Local data:Stack<Int> = New Stack<Int>()

		heightInTiles = rows.Length()

		While (row < heightInTiles)
			columns = rows[row].Split(",")

			If (columns.Length() <= 1) Then
				heightInTiles = heightInTiles - 1
				Continue
			End If

			If (widthInTiles = 0) Then
				widthInTiles = columns.Length()
			End If

			column = 0

			While (column < widthInTiles)
				data.Push(Int(columns[column]))
				column += 1
			Wend

			row += 1
		Wend

		_data = data.ToArray()

		Local i:Int

		totalTiles = widthInTiles * heightInTiles

		If (auto > OFF) Then
			_startingIndex = 1
			drawIndex = 1
			collideIndex = 1
			i = 0

			While (i < totalTiles)
				_AutoTile(i)
				i += 1
			Wend
		End If

		_tiles = FlxG.AddBitmap(tileGraphic, _TileLoader)

		_tileWidth = tileWidth
		If (_tileWidth = 0) _tileWidth = _tiles.Height()

		_tileHeight = tileHeight
		If (_tileHeight = 0) _tileHeight = _tileWidth

		i = 0
		Local l:Int = (_tiles.Width() / _tileWidth) * (_tiles.Height() / _tileHeight)

		If (auto > OFF) l += 1
		_tileObjects = _tileObjects.Resize(l)

		While (i < l)
			If (i >= collideIndex) Then
				_tileObjects[i] = New FlxTile(Self, i, _tileWidth, _tileHeight, (i >= drawIndex), allowCollisions)
			Else
				_tileObjects[i] = New FlxTile(Self, i, _tileWidth, _tileHeight, (i >= drawIndex), NONE)
			End If

			i += 1
		Wend

		width = widthInTiles * _tileWidth
		height = heightInTiles * _tileHeight
		_rects = _rects.Resize(totalTiles)
		i = 0

		While (i < totalTiles)
			_UpdateTile(i)
			i += 1
		Wend

		_buffers.Clear()

		Return Self
	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.ID)) Return

		_buffer = _buffers.Get(_camera.ID)

		If (_buffer = Null) Then
			_buffer = New FlxTilemapBuffer(_tileWidth, _tileHeight, widthInTiles, heightInTiles, _camera)
			_buffers.Set(_camera.ID, _buffer)
		End If

		_screenRows = _buffer.rows
		_screenColumns = _buffer.columns

		If (Not _buffer.dirty) Then
			_point.x = x - Int(_camera.scroll.x * scrollFactor.x) + _buffer.x
			_point.y = y - Int(_camera.scroll.y * scrollFactor.y) + _buffer.y

			_buffer.dirty = _point.x > 0 Or _point.y > 0 Or _point.x + _buffer.width < _camera.Width Or _point.y + _buffer.height < _camera.Height
		End If

		If (_buffer.dirty) Then
			_point.x = Int(_camera.scroll.x * scrollFactor.x) - x
			_point.y = Int(_camera.scroll.y * scrollFactor.y) - y

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

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

			If (_screenXInTiles < 0) _screenXInTiles = 0
			If (_screenXInTiles > widthInTiles - _screenColumns) _screenXInTiles = widthInTiles - _screenColumns
			If (_screenYInTiles < 0) _screenYInTiles = 0
			If (_screenYInTiles > heightInTiles - _screenRows) _screenYInTiles = heightInTiles - _screenRows

			_buffer.x = _screenXInTiles * _tileWidth
			_buffer.y = _screenYInTiles * _tileHeight
		End If

		_point.x = x - Int(_camera.scroll.x * scrollFactor.x) + _buffer.x
		_point.y = y - Int(_camera.scroll.y * scrollFactor.y) + _buffer.y

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

		If (FlxG._LastDrawingColor <> _camera.Color) Then
			SetColor(_camera._color.r, _camera._color.g, _camera._color.b)
			FlxG._LastDrawingColor = _camera.Color
		End If

		Local alpha:Float = _camera._color.a * _camera.Alpha

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

		PushMatrix()
			Translate( _point.x, _point.y)

			Local rowIndex:Int = _screenYInTiles * widthInTiles + _screenXInTiles
			Local row:Int = 0
			Local column:Int
			Local columnIndex:Int

			_point.y = 0

			If (_buffer.scaleFixX = 1 Or _buffer.scaleFixY = 1) Then
				While (row < _screenRows)
					columnIndex = rowIndex
					column = 0
					_point.x = 0

					While (column < _screenColumns)
						_rect = _rects[columnIndex]

						If (_rect <> Null) Then
							DrawImageRect(_tiles, _point.x, _point.y, _rect.x, _rect.y, _rect.width, _rect.height)

							If (FlxG.VisualDebug And Not ignoreDrawDebug) Then
								'TODO
							End If
						End If

						_point.x += _tileWidth
						column += 1
						columnIndex += 1
					Wend

					rowIndex += widthInTiles
					_point.y += _tileHeight
					row += 1
				Wend
			Else
				row = _screenRows - 1
				_point.y = row * _tileHeight
				rowIndex += widthInTiles * row

				While (row >= 0)
					column = _screenColumns - 1
					_point.x = column * _tileWidth
					columnIndex = rowIndex + column

					While (column >= 0)
						_rect = _rects[columnIndex]

						If (_rect <> Null) Then
							DrawImageRect(_tiles, _point.x, _point.y, _rect.x, _rect.y, _rect.width, _rect.height, 0, _buffer.scaleFixX, _buffer.scaleFixY)

							If (FlxG.VisualDebug And Not ignoreDrawDebug) Then
								'TODO
							End If
						End If

						_point.x -= _tileWidth
						column -= 1
						columnIndex -= 1
					Wend

					rowIndex -= widthInTiles
					_point.y -= _tileHeight
					row -= 1
				Wend
			End If
		PopMatrix()

		_VisibleCount += 1
	End Method

	Method GetData:Int[](simple:Bool = False)
		If (Not simple) Return _data

		Local i:Int = 0
		Local l:Int = _data.Length()
		Local data:Int[l]

		While (i < l)
			If (_tileObjects[_data[i]].allowCollisions > 0) Then
				data[i] = 1
			Else
				data[i] = 0
			End If

			i += 1
		Wend

		Return data
	End Method

	Method SetDirty:Void(dirty:Bool = True)
		For Local buffer:FlxTilemapBuffer = EachIn _buffers.Values()
			buffer.dirty = dirty
		Next
	End Method

	Method FindPath:FlxPath(start:FlxPoint, endPoint:FlxPoint, simplify:Bool = True, raySimplify:Bool = False)
		Local startIndex:Int = Int((start.y - y) / _tileHeight) * widthInTiles + Int((start.x - x) / _tileWidth)
		Local endIndex:Int = Int((endPoint.y - y) / _tileHeight) * widthInTiles + Int((endPoint.x - x) / _tileWidth)

		If (_tileObjects[_data[startIndex]].allowCollisions > 0 Or _tileObjects[_data[endIndex]].allowCollisions > 0) Then
			Return Null
		End If

		Local distances:Int[] = _ComputePathDistance(startIndex, endIndex)
		If (distances.Length() = 0) Return Null

		Local points:Stack<FlxPoint> = New Stack<FlxPoint>()
		_WalkPath(distances, endIndex, points)

		Local node:FlxPoint

		node = points.Top()
		node.x = start.x
		node.y = start.y

		node = points.Get(0)
		node.x = endPoint.x
		node.y = endPoint.y

		If (simplify) _SimplifyPath(points)
		If (raySimplify) _RaySimplifyPath(points)

		Local path:FlxPath = New FlxPath()
		Local i:Int = points.Length() - 1

		While (i >= 0)
			node = points.Get(i)

			If (node <> Null) Then
				path.AddPoint(node, true)
			End If

			i -= 1
		Wend

		Return path
	End Method

	Method Overlaps:Bool(objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local basic:FlxBasic
			Local i:Int = 0
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local l:Int = members.Length()

			While (i < l)
				basic = members[i]

				If (FlxObject(basic) <> Null) Then
					If (OverlapsWithCallback(FlxObject(basic))) Then
						results = True
					End If

				Else
					If (Overlaps(basic, inScreenSpace, camera)) Then
						results = True
					End If
				End If

				i += 1
			Wend

			Return results

		ElseIf (FlxObject(objectOrGroup) <> Null) Then
			Return OverlapsWithCallback(FlxObject(objectOrGroup))
		End If

		Return False
	End Method

	Method OverlapsAt:Bool(x:Float, y:Float, objectOrGroup:FlxBasic, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (FlxGroup(objectOrGroup) <> Null) Then
			Local results:Bool = False
			Local basic:FlxBasic
			Local i:Int = 0
			Local members:FlxBasic[] = FlxGroup(objectOrGroup).Members
			Local l:Int = members.Length()

			While (i < l)
				basic = members[i]

				If (FlxObject(basic) <> Null) Then
					_point.x = x
					_point.y = y

					If (OverlapsWithCallback(FlxObject(basic), Null, False, _point)) Then
						results = True
					End If

				Else
					If (OverlapsAt(x, y, basic, inScreenSpace, camera)) Then
						results = True
					End If
				End If

				i += 1
			Wend

			Return results

		ElseIf (FlxObject(objectOrGroup) <> Null) Then
			_point.x = x
			_point.y = y

			Return OverlapsWithCallback(FlxObject(objectOrGroup), Null, False, _point)
		End If

		Return False
	End Method

	Method OverlapsWithCallback:Bool(object:FlxObject, callback:FlxTileOverlapChecker = Null, flipCallbackParams:Bool = False, position:FlxPoint = Null)
		Local results:Bool = False

		Local lx:Float = x
		Local ly:Float = y

		If (position <> Null) Then
			lx = position.x
			ly = position.y
		End If

		Local selectionX:Int = Floor((object.x - lx) / _tileWidth)
		Local selectionY:Int = Floor((object.y - ly) / _tileHeight)
		Local selectionWidth:Int = selectionX + Ceil(object.width / _tileWidth) + 1
		Local selectionHeight:Int = selectionY + Ceil(object.height / _tileHeight) + 1

		If (selectionX < 0) selectionX = 0
		If (selectionY < 0) selectionY = 0
		If (selectionWidth > widthInTiles) selectionWidth = widthInTiles
		If (selectionHeight > heightInTiles) selectionHeight = heightInTiles

		Local rowStart:Int = selectionY * widthInTiles
		Local row:Int = selectionY
		Local column:Int
		Local tile:FlxTile
		Local overlapFound:Bool
		Local deltaX:Float = lx - last.x
		Local deltaY:Float = ly - last.y

		While (row < selectionHeight)
			column = selectionX

			While (column < selectionWidth)
				overlapFound = False
				tile = _tileObjects[_data[rowStart + column]]

				If (tile.allowCollisions) Then
					tile.x = lx + column * _tileWidth
					tile.y = ly + row * _tileHeight
					tile.last.x = tile.x - deltaX
					tile.last.y = tile.y - deltaY

					If (callback <> Null) Then
						If (flipCallbackParams) Then
							overlapFound = callback.IsTileOverlap(object, tile)
						Else
							overlapFound = callback.IsTileOverlap(tile, object)
						End If
					Else
						overlapFound = (object.x + object.width > tile.x And object.x < tile.x + tile.width And object.y + object.height > tile.y And object.y < tile.y + tile.height)
					End If

					If (overlapFound) Then
						If (tile.callback <> Null And (tile.filter = Null Or object.GetClass().ExtendsClass(tile.filter))) Then
							tile.mapIndex = rowStart + column
							tile.callback.OnTileHit(tile, object)
						End If

						results = True
					End If

				ElseIf (tile.callback <> Null And (tile.filter = Null Or object.GetClass().ExtendsClass(tile.filter))) Then
					tile.mapIndex = rowStart + column
					tile.callback.OnTileHit(tile, object)
				End If

				column += 1
			Wend

			rowStart += widthInTiles
			row += 1
		Wend

		Return results
	End Method

	Method OverlapsPoint:Bool(point:FlxPoint, inScreenSpace:Bool = False, camera:FlxCamera = Null)
		If (Not inScreenSpace) Then
			Return _tileObjects[_data[Int(Int((point.y - y) / _tileHeight) * widthInTiles + (point.x - x) / _tileWidth)]].allowCollisions > 0
		End If

		If (camera = Null) camera = FlxG.Camera

		point.x -= camera.scroll.x
		point.y -= camera.scroll.y

		GetScreenXY(_point, camera)

		Return _tileObjects[_data[Int(Int((point.y - _point.y) / _tileHeight) * widthInTiles + (point.x - _point.x) / _tileWidth)]].allowCollisions > 0
	End Method

	Method GetTile:Int(x:Int, y:Int)
		Return _data[y * widthInTiles + x]
	End Method

	Method GetTileByIndex:Int(index:Int)
		Return _data[index]
	End Method

	Method GetTileInstances:Stack<Int>(index:Int)
		Local elements:Stack<Int> = Null

		Local i:Int = 0
		Local l:Int = widthInTiles * heightInTiles

		While (i < l)
			If (_data[i] = index) Then
				If (elements = Null) elements = New Stack<Int>()
				elements.Push(i)
			End If

			i += 1
		Wend

		Return elements
	End Method

	Method GetTileCoords:Stack<FlxPoint>(index:Int, midpoint:Bool = True)
		Local elements:Stack<FlxPoint> = Null

		Local point:FlxPoint
		Local i:Int = 0
		Local l:Int = widthInTiles * heightInTiles

		While (i < l)
			If (_data[i] = index) Then
				point = New FlxPoint(x + Int(i Mod widthInTiles) * _tileWidth, y + Int(i / widthInTiles) * _tileHeight)

				If (midpoint) Then
					point.x += _tileWidth * .5
					point.y += _tileHeight * .5
				End If

				If (elements = Null) elements = New Stack<FlxPoint>()
				elements.Push(point)
			End If

			i += 1
		Wend

		Return elements
	End Method

	Method SetTile:Bool(x:Int, y:Int, tile:Int, updateGraphics:Bool = True)
		If (x >= widthInTiles Or y >= heightInTiles) Then
			Return False
		End If

		Return SetTileByIndex(y * widthInTiles + x, tile, updateGraphics)
	End Method

	Method SetTileByIndex:Bool(index:Int, tile:Int, updateGraphics:Bool = True)
		If (index >= _data.Length()) Return False

		_data[index] = tile

		If (Not updateGraphics) Return True

		If (auto = OFF) Then
			_UpdateTile(index)
			Return True
		End If

		Local i:Int = 0
		Local row:Int = Int(index / widthInTiles) - 1
		Local rowLength:Int = row + 3
		Local column:Int = index Mod widthInTiles - 1
		Local columnHeight:Int = column + 3

		While (row < rowLength)
			column = columnHeight - 3

			While (column < columnHeight)
				If (row >= 0 And row < heightInTiles And column >= 0 And column < widthInTiles) Then
					i = row * widthInTiles + column
					_AutoTile(i)
					_UpdateTile(i)
				End If

				column += 1
			Wend

			row += 1
		Wend

		Return True
	End Method

	Method SetTileProperties:Void(tile:Int, allowCollisions:Int = $1111, callback:FlxTileHitListener = Null, callbackFilter:ClassInfo = Null, range:Int = 1)
		If (range <= 0) range = 1

		Local tileObject:FlxTile
		Local i:Int = tile
		Local l:Int = tile + range

		While (i < l)
			tileObject = _tileObjects[i]

			tileObject.allowCollisions = allowCollisions
			tileObject.callback = callback
			tileObject.filter = callbackFilter

			i += 1
		Wend
	End Method

	Method Follow:Void(camera:FlxCamera = Null, border:Int = 0, updateWorld:Bool = True)
		If (camera = Null) camera = FlxG.Camera
		camera.SetBounds(x + border * _tileWidth, y + border * _tileHeight, width - border * _tileWidth * 2, height - border * _tileHeight * 2, updateWorld)
	End Method

	Method GetBounds:FlxRect(bounds:FlxRect = Null)
		If (bounds = Null) bounds = New FlxRect()
		Return bounds.Make(x, y, width, height)
	End Method

	Method Ray:Bool(startPoint:FlxPoint, endPoint:FlxPoint, result:FlxPoint = Null, resolution:Float = 1)
		Local _step:Float = _tileWidth

		If (_tileHeight < _tileWidth) Then
			_step = _tileHeight
		End If

		_step /= resolution

		Local deltaX:Float = endPoint.x - startPoint.x
		Local deltaY:Float = endPoint.y - startPoint.y
		Local distance:Float = Sqrt(deltaX * deltaX + deltaY * deltaY)
		Local steps:Int = Ceil(distance / _step)
		Local stepX:Float = deltaX / steps
		Local stepY:Float = deltaY / steps
		Local curX:Float = startPoint.x - stepX - x
		Local curY:Float = startPoint.y - stepY - y
		Local tileX:Int
		Local tileY:Int
		Local i:Int = 0

		While (i < steps)
			curX += stepX
			curY += stepY

			If (curX < 0 Or curX > width Or curY < 0 Or curY > height) Then
				i += 1
				Continue
			End If

			tileX = curX / _tileWidth
			tileY = curY / _tileHeight

			If (_tileObjects[_data[tileY * widthInTiles + tileX]].allowCollisions) Then
				tileX *= _tileWidth
				tileY *= _tileHeight

				Local rx:Float = 0
				Local ry:Float = 0
				Local q:Float
				Local lx:Float = curX - stepX
				Local ly:Float = curY - stepY

				q = tileX
				If (deltaX < 0) q += _tileWidth

				rx = q
				ry = ly + stepY * ((q - lx) / stepX)

				If (ry > tileY And ry < tileY + _tileHeight) Then
					If (result = Null) Then
						result = New FlxPoint()
					End If

					result.x = rx
					result.y = ry

					Return False
				End If

				q = tileY
				If (deltaY < 0) q += _tileHeight

				rx = lx + stepX * ((q - ly) / stepY)
				ry = q

				If (rx > tileX And rx < tileX + _tileWidth) Then
					If (result = Null) Then
						result = New FlxPoint()
					End If

					result.x = rx
					result.y = ry

					Return False
				End If

				Return True
			End If

			i += 1
		Wend

		Return True
	End Method

	Function ArrayToCSV:String(data:Int[], width:Int, invert:Bool = False)
		Local row:Int = 0
		Local column:Int
		Local csv:StringStack = New StringStack()
		Local height:Int = data.Length() / width
		Local index:Int

		While (row < height)
			column = 0

			While (column < width)
				index = data[row * width + column]

				If (invert) Then
					If (index = 0) Then
						index = 1
					Else
						index = 0
					End If
				End If

				If (column = 0) Then
					If (row = 0) Then
						csv.Push(index)
					Else
						csv.Push("~n" + index)
					End If
				Else
					csv.Push(", " + index)
				End If

				column += 1
			Wend

			row += 1
		Wend

		Return csv.Join("")
	End Function

Private
	Method _SimplifyPath:Void(points:Stack<FlxPoint>)
		Local deltaPrevious:Float
		Local deltaNext:Float
		Local last:FlxPoint = points.Get(0)
		Local node:FlxPoint
		Local nextPoint:FlxPoint
		Local i:Int = 1
		Local l:Int = points.Length() - 1

		While (i < l)
			node = points.Get(i)
			nextPoint = points.Get(i + 1)

			deltaPrevious = (node.x - last.x) / (node.y - last.y)
			deltaNext = (node.x - nextPoint.x) / (node.y - nextPoint.y)

			If (last.x = nextPoint.x Or last.y = nextPoint.y Or deltaPrevious = deltaNext) Then
				points.Set(i, Null)
			Else
				last = node
			End If

			i += 1
		Wend
	End Method

	Method _RaySimplifyPath:Void(points:Stack<FlxPoint>)
		Local source:FlxPoint = points.Get(0)
		Local lastIndex:Int = -1
		Local node:FlxPoint
		Local i:Int = 1
		Local l:Int = points.Length()

		While (i < l)
			node = points.Get(i)
			i += 1

			If (node = Null) Continue

			If (Ray(source, node, _point)) Then
				If (lastIndex >= 0) points.Set(lastIndex, Null)
			Else
				source = points.Get(lastIndex)
			End If

			lastIndex = i - 1
		Wend
	End Method

	Method _ComputePathDistance:Int[](startIndex:Int, endIndex:Int)
		Local mapSize:Int = widthInTiles * heightInTiles
		Local distances:Int[mapSize]
		Local i:Int = 0

		While (i < mapSize)
			If (_tileObjects[_data[i]].allowCollisions) Then
				distances[i] = -2
			Else
				distances[i] = -1
			End If

			i += 1
		Wend

		distances[startIndex] = 0

		Local distance:Int = 1
		Local neighbors:Stack<Int> = New Stack<Int>([startIndex])
		Local current:Stack<Int>
		Local currentIndex:Int
		Local left:Bool
		Local right:Bool
		Local up:Bool
		Local down:Bool
		Local currentLength:Int
		Local foundEnd:Bool = False

		While (neighbors.Length() > 0)
			current = neighbors
			neighbors = New Stack<Int>()

			i = 0
			currentLength = current.Length()

			While (i < currentLength)
				currentIndex = current.Get(i)

				If (currentIndex = endIndex) Then
					foundEnd = True
					neighbors.Clear()
					Exit
				End If

				left = currentIndex Mod widthInTiles > 0
				right = currentIndex Mod widthInTiles < widthInTiles - 1
				up = currentIndex / widthInTiles > 0
				down = currentIndex / widthInTiles < heightInTiles - 1

				Local index:Int

				If (up) Then
					index = currentIndex - widthInTiles

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (right) Then
					index = currentIndex + 1

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (down) Then
					index = currentIndex + widthInTiles

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (left) Then
					index = currentIndex - 1

					If (distances[index] = -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (up And right) Then
					index = currentIndex - widthInTiles + 1

					If (distances[index] = -1 And distances[currentIndex - widthInTiles] >= -1 And distances[currentIndex + 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (right And down) Then
					index = currentIndex + widthInTiles + 1

					If (distances[index] = -1 And distances[currentIndex + widthInTiles] >= -1 And distances[currentIndex + 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (left And down) Then
					index = currentIndex + widthInTiles - 1

					If (distances[index] = -1 And distances[currentIndex + widthInTiles] >= -1 And distances[currentIndex - 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				If (up And left) Then
					index = currentIndex - widthInTiles - 1

					If (distances[index] = -1 And distances[currentIndex - widthInTiles] >= -1 And distances[currentIndex - 1] >= -1) Then
						distances[index] = distance
						neighbors.Push(index)
					End If
				End If

				i += 1
			Wend

			distance += 1
		Wend

		If (Not foundEnd) distances = []

		Return distances
	End Method

	Method _WalkPath:Void(data:Int[], start:Int, points:Stack<FlxPoint>)
		points.Push(New FlxPoint(x + Int(start Mod widthInTiles) * _tileWidth + _tileWidth * .5, y + Int(start / widthInTiles) * _tileHeight + _tileHeight * .5))
		If (data[start] = 0) Return

		Local left:Bool = start Mod widthInTiles > 0
		Local right:Bool = start Mod widthInTiles < widthInTiles - 1
		Local up:Bool = start / widthInTiles > 0
		Local down:Bool = start / widthInTiles < heightInTiles - 1

		Local current:Int = data[start]
		Local i:Int

		If (up) Then
			i = start - widthInTiles

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (right) Then
			i = start + 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (down) Then
			i = start + widthInTiles

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (left) Then
			i = start - 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (up And right) Then
			i = start - widthInTiles + 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (right And down) Then
			i = start + widthInTiles + 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (left And down) Then
			i = start + widthInTiles - 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If

		If (up And left) Then
			i = start - widthInTiles - 1

			If (data[i] >= 0 And data[i] < current) Then
				_WalkPath(data, i, points)
				Return
			End If
		End If
	End Method

	Method _AutoTile:Void(index:Int)
		If (_data[index] = 0) Return

		_data[index] = 0

		If (index - widthInTiles < 0 Or _data[index - widthInTiles] > 0) Then
			_data[index] += 1
		End If

		If (index Mod widthInTiles >= widthInTiles - 1 Or _data[index + 1] > 0) Then
			_data[index] += 2
		End If

		If (index + widthInTiles >= totalTiles Or _data[index + widthInTiles] > 0) Then
			_data[index] += 4
		End If

		If (index Mod widthInTiles <= 0 Or _data[index - 1] > 0) Then
			_data[index] += 8
		End If

		If (auto = ALT And _data[index] = 15) Then
			If (index Mod widthInTiles > 0 And index + widthInTiles < totalTiles And _data[index + widthInTiles - 1] <= 0) Then
				_data[index] = 1
			End If

			If (index Mod widthInTiles > 0 And index - widthInTiles >= 0 And _data[index - widthInTiles - 1] <= 0) Then
				_data[index] = 2
			End If

			If (index Mod widthInTiles < widthInTiles - 1 And index - widthInTiles >= 0 And _data[index - widthInTiles + 1] <= 0) Then
				_data[index] = 4
			End If

			If (index Mod widthInTiles < widthInTiles - 1 And index + widthInTiles >= totalTiles And _data[index + widthInTiles + 1] <= 0) Then
				_data[index] = 8
			End If
		End If

		_data[index] += 1
	End Method

	Method _UpdateTile:Void(index:Int)
		Local tile:FlxTile = _tileObjects[_data[index]]

		If (tile = Null Or Not tile.visible) Then
			_rects[index] = Null
			Return
		End If

		Local rx:Int = (_data[index] - _startingIndex) * _tileWidth
		Local ry:Int = 0

		If (rx >= _tiles.Width()) Then
			ry = Int(rx / _tiles.Width()) * _tileHeight
			rx Mod= _tiles.Width()
		End If

		_rects[index] = New FlxRect(rx, ry, _tileWidth, _tileHeight)
	End Method

End Class

Private
Class FlxTileLoader Extends FlxResourceLoader<Image>

	Method Load:Image(name:String)
		Return LoadImage(FlxAssetsManager.GetImagePath(name))
	End Method

End Class


add missing tests for Target

add missing tests for Target

Strict

Private

Import bono

Public

Class TargetTest Extends TestCase
    Method TestIsMarketGoogle:Void()
        #If TARGET="android"
            #If BONO_ANDROID_MARKET="google"
                AssertTrue(Target.IS_MARKET_GOOGLE)
                AssertFalse(Target.IS_MARKET_AMAZON)
                AssertFalse(Target.IS_MARKET_SAMSUNG)
            #Else
                AssertFalse(Target.IS_MARKET_GOOGLE)
            #End
        #Else
            AssertFalse(Target.IS_MARKET_GOOGLE)
            AssertFalse(Target.IS_MARKET_AMAZON)
            AssertFalse(Target.IS_MARKET_SAMSUNG)
        #End
    End

    Method TestIsMarketAmazon:Void()
        #If TARGET="android"
            #If BONO_ANDROID_MARKET="amazon"
                AssertFalse(Target.IS_MARKET_GOOGLE)
                AssertTrue(Target.IS_MARKET_AMAZON)
                AssertFalse(Target.IS_MARKET_SAMSUNG)
            #Else
                AssertFalse(Target.IS_MARKET_AMAZON)
            #End
        #Else
            AssertFalse(Target.IS_MARKET_GOOGLE)
            AssertFalse(Target.IS_MARKET_AMAZON)
            AssertFalse(Target.IS_MARKET_SAMSUNG)
        #End
    End

    Method TestIsMarketSamsung:Void()
        #If TARGET="android"
            #If BONO_ANDROID_MARKET="samsung"
                AssertFalse(Target.IS_MARKET_GOOGLE)
                AssertFalse(Target.IS_MARKET_AMAZON)
                AssertTrue(Target.IS_MARKET_SAMSUNG)
            #Else
                AssertFalse(Target.IS_MARKET_SAMSUNG)
            #End
        #Else
            AssertFalse(Target.IS_MARKET_GOOGLE)
            AssertFalse(Target.IS_MARKET_AMAZON)
            AssertFalse(Target.IS_MARKET_SAMSUNG)
        #End
    End

    Method TestPlatformId:Void()
        #If TARGET="android"
            AssertStringStartsWith("android:", Target.PLATFORM_ID)

            If Target.IS_MARKET_GOOGLE
                AssertStringEndsWith(":google", Target.PLATFORM_ID)
            ElseIf Target.IS_MARKET_SAMSUNG
                AssertStringEndsWith(":samsung", Target.PLATFORM_ID)
            ElseIf Target.IS_MARKET_AMAZON
                AssertStringEndsWith(":amazon", Target.PLATFORM_ID)
            Else
                MarkTestIncomplete("unknown android market")
            End
        #ElseIf TARGET="glfw" Or TARGET="stdcpp"
            AssertStringStartsWith(Target.HOST + ":", Target.PLATFORM_ID)
            AssertStringEndsWith(":" + Target.TARGET, Target.PLATFORM_ID)
        #Else
            AssertEquals(Target.TARGET, Target.PLATFORM_ID)
        #End
    End

    Method TestLang:Void()
        #If LANG="js"
            AssertEquals("js", Target.LANG)
        #ElseIf LANG="as"
            AssertEquals("as", Target.LANG)
        #ElseIf LANG="java"
            AssertEquals("java", Target.LANG)
        #ElseIf LANG="cpp"
            AssertEquals("cpp", Target.LANG)
        #End
    End

    Method TestHost:Void()
        #If HOST="winnt"
            AssertEquals("winnt", Target.HOST)
            AssertTrue(Target.IS_WIN)
            AssertFalse(Target.IS_MAC)
        #ElseIf HOST="macos"
            AssertEquals("macos", Target.HOST)
            AssertFalse(Target.IS_WIN)
            AssertTrue(Target.IS_MAC)
        #End
    End

    Method TestConfig:Void()
        #If CONFIG="debug"
            AssertEquals("debug", Target.CONFIG)
            AssertTrue(Target.IS_DEBUG)
            AssertFalse(Target.IS_RELEASE)
        #Else
            AssertEquals("release", Target.CONFIG)
            AssertFalse(Target.IS_DEBUG)
            AssertTrue(Target.IS_RELEASE)
        #End
    End

    Method TestAndroid:Void()
        #If TARGET="android"
            AssertEquals("android", Target.TARGET)
            AssertTrue(Target.IS_ANDROID)
        #Else
            AssertFalse(Target.IS_ANDROID)
        #End
    End

    Method TestFlash:Void()
        #If TARGET="flash"
            AssertEquals("flash", Target.TARGET)
            AssertTrue(Target.IS_FLASH)
        #Else
            AssertFalse(Target.IS_FLASH)
        #End
    End

    Method TestGLFW:Void()
        #If TARGET="glfw"
            AssertEquals("glfw", Target.TARGET)
            AssertTrue(Target.IS_GLFW)
        #Else
            AssertFalse(Target.IS_GLFW)
        #End
    End

    Method TestHTML5:Void()
        #If TARGET="html5"
            AssertEquals("html5", Target.TARGET)
            AssertTrue(Target.IS_HTML5)
        #Else
            AssertFalse(Target.IS_HTML5)
        #End
    End

    Method TestIos:Void()
        #If TARGET="ios"
            AssertEquals("ios", Target.TARGET)
            AssertTrue(Target.IS_IOS)
        #Else
            AssertFalse(Target.IS_IOS)
        #End
    End

    Method TestMetro:Void()
        #If TARGET="metro"
            AssertEquals("metro", Target.TARGET)
            AssertTrue(Target.IS_METRO)
        #Else
            AssertFalse(Target.IS_METRO)
        #End
    End

    Method TestPSM:Void()
        #If TARGET="psm"
            AssertEquals("psm", Target.TARGET)
            AssertTrue(Target.IS_PSM)
        #Else
            AssertFalse(Target.IS_PSM)
        #End
    End

    Method TestStdCpp:Void()
        #If TARGET="stdcpp"
            AssertEquals("stdcpp", Target.TARGET)
            AssertTrue(Target.IS_STDCPP)
        #Else
            AssertFalse(Target.IS_STDCPP)
        #End
    End

    Method TestXna:Void()
        #If TARGET="xna"
            AssertEquals("xna", Target.TARGET)
            AssertTrue(Target.IS_XNA)
        #Else
            AssertFalse(Target.IS_XNA)
        #End
    End
End

Strict

Private

Import bono

Public

Class TargetTest Extends TestCase
    Method TestIsMarketGoogle:Void()
        #If TARGET="android"
            #If BONO_ANDROID_MARKET="google"
                AssertTrue(Target.IS_MARKET_GOOGLE)
                AssertFalse(Target.IS_MARKET_AMAZON)
            #Else
                AssertFalse(Target.IS_MARKET_GOOGLE)
                AssertTrue(Target.IS_MARKET_AMAZON)
            #End
        #Else
            AssertFalse(Target.IS_MARKET_GOOGLE)
            AssertFalse(Target.IS_MARKET_AMAZON)
        #End
    End

    Method TestIsMarketAmazon:Void()
        #If TARGET="android"
            #If BONO_ANDROID_MARKET="amazon"
                AssertFalse(Target.IS_MARKET_GOOGLE)
                AssertTrue(Target.IS_MARKET_AMAZON)
            #Else
                AssertTrue(Target.IS_MARKET_GOOGLE)
                AssertFalse(Target.IS_MARKET_AMAZON)
            #End
        #Else
            AssertFalse(Target.IS_MARKET_GOOGLE)
            AssertFalse(Target.IS_MARKET_AMAZON)
        #End
    End

    Method TestLang:Void()
        #If LANG="js"
            AssertEquals("js", Target.LANG)
        #ElseIf LANG="as"
            AssertEquals("as", Target.LANG)
        #ElseIf LANG="java"
            AssertEquals("java", Target.LANG)
        #ElseIf LANG="cpp"
            AssertEquals("cpp", Target.LANG)
        #End
    End

    Method TestHost:Void()
        #If HOST="winnt"
            AssertEquals("winnt", Target.HOST)
            AssertTrue(Target.IS_WIN)
            AssertFalse(Target.IS_MAC)
        #ElseIf HOST="macos"
            AssertEquals("macos", Target.HOST)
            AssertFalse(Target.IS_WIN)
            AssertTrue(Target.IS_MAC)
        #End
    End

    Method TestConfig:Void()
        #If CONFIG="debug"
            AssertEquals("debug", Target.CONFIG)
            AssertTrue(Target.IS_DEBUG)
            AssertFalse(Target.IS_RELEASE)
        #Else
            AssertEquals("release", Target.CONFIG)
            AssertFalse(Target.IS_DEBUG)
            AssertTrue(Target.IS_RELEASE)
        #End
    End

    Method TestAndroid:Void()
        #If TARGET="android"
            AssertEquals("android", Target.TARGET)
            AssertTrue(Target.IS_ANDROID)
        #Else
            AssertFalse(Target.IS_ANDROID)
        #End
    End

    Method TestFlash:Void()
        #If TARGET="flash"
            AssertEquals("flash", Target.TARGET)
            AssertTrue(Target.IS_FLASH)
        #Else
            AssertFalse(Target.IS_FLASH)
        #End
    End

    Method TestGLFW:Void()
        #If TARGET="glfw"
            AssertEquals("glfw", Target.TARGET)
            AssertTrue(Target.IS_GLFW)
        #Else
            AssertFalse(Target.IS_GLFW)
        #End
    End

    Method TestHTML5:Void()
        #If TARGET="html5"
            AssertEquals("html5", Target.TARGET)
            AssertTrue(Target.IS_HTML5)
        #Else
            AssertFalse(Target.IS_HTML5)
        #End
    End

    Method TestIos:Void()
        #If TARGET="ios"
            AssertEquals("ios", Target.TARGET)
            AssertTrue(Target.IS_IOS)
        #Else
            AssertFalse(Target.IS_IOS)
        #End
    End

    Method TestMetro:Void()
        #If TARGET="metro"
            AssertEquals("metro", Target.TARGET)
            AssertTrue(Target.IS_METRO)
        #Else
            AssertFalse(Target.IS_METRO)
        #End
    End

    Method TestPSM:Void()
        #If TARGET="psm"
            AssertEquals("psm", Target.TARGET)
            AssertTrue(Target.IS_PSM)
        #Else
            AssertFalse(Target.IS_PSM)
        #End
    End

    Method TestStdCpp:Void()
        #If TARGET="stdcpp"
            AssertEquals("stdcpp", Target.TARGET)
            AssertTrue(Target.IS_STDCPP)
        #Else
            AssertFalse(Target.IS_STDCPP)
        #End
    End

    Method TestXna:Void()
        #If TARGET="xna"
            AssertEquals("xna", Target.TARGET)
            AssertTrue(Target.IS_XNA)
        #Else
            AssertFalse(Target.IS_XNA)
        #End
    End
End


Signals string map replaced by int map

Signals string map replaced by int map

Strict

Import reflection
Import system.signals.flxsignal

Class FlxSignals

Private
	Field _signals:IntMap<FlxSignal>

	Field _methodSlots:List<FlxMethodSlot>

Public
	Method New()
		_signals = New IntMap<FlxSignal>()
		_methodSlots = New List<FlxMethodSlot>()
	End Method

	Method Connect:Void(signalID:Int, listener:FlxSignalListener)
		GetSignal(signalID).RegisterListener(listener)
	End Method

	Method Connect:Void(signalID:Int, listener:FlxSignalListener, instant:Bool)
		GetSignal(signalID).RegisterListener(listener, instant)
	End Method

	Method Connect:Void(signalID:Int, listener:FlxSignalListener, instant:Bool, priority:Int)
		GetSignal(signalID).RegisterListener(listener, instant, priority)
	End Method

	Method Connect:Void(signalID:Int, methodName:String, context:Object)
		Connect(signalID, GetMethodSlot(methodName, context))
	End Method

	Method Connect:Void(signalID:Int, methodName:String, context:Object, instant:Bool)
		Connect(signalID, GetMethodSlot(methodName, context), instant)
	End Method

	Method Connect:Void(signalID:Int, methodName:String, context:Object, instant:Bool, priority:Int)
		Connect(signalID, GetMethodSlot(methodName, context), instant, priority)
	End Method

	Method Emit:Void(signalID:Int, data:Object = Null)
		GetSignal(signalID).Emit(data)
	End Method

Private
	Method GetSignal:FlxSignal(signalID:Int)
		Local s:FlxSignal = _signals.Get(signalID)

		If (s = Null) Then
			s = New FlxSignal()
			_signals.Set(signalID, s)
		End If

		Return s
	End Method

	Method GetMethodSlot:FlxMethodSlot(methodName:String, context:Object)
		Local classInfo:ClassInfo = GetClass(context)

		If (classInfo <> Null) Then
			Local methodInfo:MethodInfo = classInfo.GetMethod(methodName,[])

			If (methodInfo <> Null) Then
				Local node:list.Node<FlxMethodSlot> = _methodSlots.FirstNode()

				While (node <> Null)
					If (node.Value()._method = methodInfo And node.Value()._context = context) Then
						Return node.Value()
					End If

					node = node.NextNode()
				Wend

				Return _methodSlots.AddLast(New FlxMethodSlot(context, methodInfo)).Value()
			End If
		End If

		Return Null
	End Method

End Class

Private

Class FlxMethodSlot Implements FlxSignalListener

Private
	Field _context:Object

	Field _method:MethodInfo

Public
	Method New(context:Object, methodInfo:MethodInfo)
		_context = context
		_method = methodInfo
	End Method

	Method OnSignalEmitted:Void(signal:FlxSignal, data:Object)
		_method.Invoke(_context,[])
	End Method

End Class

Strict

Import reflection
Import system.signals.flxsignal

Class FlxSignals

Private
	Field _signals:StringMap<FlxSignal>

	Field _methodSlots:List<FlxMethodSlot>

Public
	Method New()
		_signals = New StringMap<FlxSignal>()
		_methodSlots = New List<FlxMethodSlot>()
	End Method

	Method Connect:Void(signal:String, listener:FlxSignalListener)
		GetSignal(signal).RegisterListener(listener)
	End Method

	Method Connect:Void(signal:String, listener:FlxSignalListener, instant:Bool)
		GetSignal(signal).RegisterListener(listener, instant)
	End Method

	Method Connect:Void(signal:String, listener:FlxSignalListener, instant:Bool, priority:Int)
		GetSignal(signal).RegisterListener(listener, instant, priority)
	End Method

	Method Connect:Void(signal:String, methodName:String, context:Object)
		Connect(signal, GetMethodSlot(methodName, context))
	End Method

	Method Connect:Void(signal:String, methodName:String, context:Object, instant:Bool)
		Connect(signal, GetMethodSlot(methodName, context), instant)
	End Method

	Method Connect:Void(signal:String, methodName:String, context:Object, instant:Bool, priority:Int)
		Connect(signal, GetMethodSlot(methodName, context), instant, priority)
	End Method

	Method Emit:Void(signal:String, data:Object = Null)
		GetSignal(signal).Emit(data)
	End Method

Private
	Method GetSignal:FlxSignal(signal:String)
		Local s:FlxSignal = _signals.Get(signal)

		If (s = Null) Then
			s = New FlxSignal()
			_signals.Add(signal, s)
		End If

		Return s
	End Method

	Method GetMethodSlot:FlxMethodSlot(methodName:String, context:Object)
		Local classInfo:ClassInfo = GetClass(context)

		If (classInfo <> Null) Then
			Local methodInfo:MethodInfo = classInfo.GetMethod(methodName,[])

			If (methodInfo <> Null) Then
				Local node:list.Node<FlxMethodSlot> = _methodSlots.FirstNode()

				While (node <> Null)
					If (node.Value()._method = methodInfo And node.Value()._context = context) Then
						Return node.Value()
					End If

					node = node.NextNode()
				Wend

				Return _methodSlots.AddLast(New FlxMethodSlot(context, methodInfo)).Value()
			End If
		End If

		Return Null
	End Method

End Class

Private

Class FlxMethodSlot Implements FlxSignalListener

Private
	Field _context:Object

	Field _method:MethodInfo

Public
	Method New(context:Object, methodInfo:MethodInfo)
		_context = context
		_method = methodInfo
	End Method

	Method OnSignalEmitted:Void(signal:FlxSignal, data:Object)
		_method.Invoke(_context,[])
	End Method

End Class


coding style cleanup

coding style cleanup

Strict

Import mojo

Class DeltaTimer
    Private

    Field currentTicks:Float
    Field frameTime:Float
    Field lastTicks:Float
    Field targetFps:Float

    Public

    Field delta:Float

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

    Method OnUpdate:Void()
        currentTicks = Millisecs()
        frameTime = currentTicks - lastTicks
        delta = frameTime / (1000.0 / targetFps)
        lastTicks = currentTicks
    End
End

Strict

Import mojo

Class DeltaTimer
    Private

    Field targetfps:Float
    Field currentticks:Float
    Field lastticks:Float
    Field frametime:Float

    Public

    Field delta:Float

    Method New(fps:Float)
        targetfps = fps
        lastticks = Millisecs()
    End

    Method OnUpdate:Void()
        currentticks = Millisecs()
        frametime = currentticks - lastticks
        delta = frametime / (1000.0 / targetfps)
        lastticks = currentticks
    End
End


#19 fixed

#19 fixed

Strict

Import flxextern
Import flxpoint
Import flxrect
Import flxbasic
Import flxobject
Import flxg

Import system.flxcolor

Class FlxCamera Extends FlxBasic

	Global ClassObject:FlxClass = New FlxCameraClass()

	Const STYLE_LOCKON:Int = 0

	Const STYLE_PLATFORMER:Int = 1

	Const STYLE_TOPDOWN:Int = 2

	Const STYLE_TOPDOWN_TIGHT:Int = 3

	Const SHAKE_BOTH_AXES:Int = 0

	Const SHAKE_HORIZONTAL_ONLY:Int = 1

	Const SHAKE_VERTICAL_ONLY:Int  = 2

	Global DefaultZoom:Float

	Field target:FlxObject

	Field deadzone:FlxRect

	Field bounds:FlxRect

	Field scroll:FlxPoint

	Field _color:FlxColor

Private
	Const _MIN_FLOAT_VALUE:Float = .0000001

	Field _x:Float

	Field _y:Float

	Field _realX:Float

	Field _realY:Float

	Field _width:Float

	Field _height:Float

	Field _realWidth:Float

	Field _realHeight:Float

	Field _zoom:Float

	Field _point:FlxPoint

	Field _alpha:Float

	Field _scaleX:Float

	Field _scaleY:Float

	Field _angle:Float

	Field _clipped:Bool

	Field _bgColor:FlxColor

	Field _fxFlashColor:Int

	Field _fxFlashDuration:Float

	Field _fxFlashComplete:FlxCameraFlashListener

	Field _fxFlashAlpha:Float

	Field _fxFadeColor:Int

	Field _fxFadeDuration:Float

	Field _fxFadeComplete:FlxCameraFadeListener

	Field _fxFadeAlpha:Float

	Field _fxShakeIntensity:Float

	Field _fxShakeDuration:Float

	Field _fxShakeComplete:FlxCameraShakeListener

	Field _fxShakeOffset:FlxPoint

	Field _fxShakeDirection:Int

	Field _fill:FlxColor

	Field _id:Int

#If TARGET = "xna"
	Field _xnaRectHelper:FlxRect
#End

	Global _Inkrement:Int

Public
	Method New(x:Int, y:Int, width:Int, height:Int, zoom:Float = 0)
		Zoom = zoom 'must be first for init
		X = x
		Y = y
		Width = width
		Height = height
		target = Null
		deadzone = Null
		scroll = New FlxPoint()
		_point = New FlxPoint()
		bounds = Null
		_bgColor = New FlxColor(FlxG.BgColor())
		_color = New FlxColor()
		_alpha = 1

		_fxFlashColor = 0
		_fxFlashDuration = 0
		_fxFlashComplete = null
		_fxFlashAlpha = 0

		_fxFadeColor = 0
		_fxFadeDuration = 0
		_fxFadeComplete = Null
		_fxFadeAlpha = 0

		_fxShakeIntensity = 0
		_fxShakeDuration = 0
		_fxShakeComplete = Null
		_fxShakeOffset = New FlxPoint()
		_fxShakeDirection = 0

		active = True

		_fill = New FlxColor(0)

		_id = _Inkrement
		_Inkrement += 1

		#If TARGET = "xna"
			_xnaRectHelper = New FlxRect()
		#End
	End Method

	Method Destroy:Void()
		target = Null
		scroll = Null
		deadzone = Null
		bounds = Null
		_color = Null
		_bgColor = Null
		_fxFlashComplete = Null
		_fxFadeComplete = Null
		_fxShakeComplete = Null
		_fxShakeOffset = Null
		_fill = Null

	#If TARGET = "xna"
		_xnaRectHelper = Null
	#End
	End Method

	Method Lock:Void()
		If (_clipped) Then
		#If TARGET = "xna"
			_xnaRectHelper.Make(_realX, _realY, _realWidth, _realHeight)

			If (_fxShakeOffset.x <> 0) Then
				_xnaRectHelper.x += _fxShakeOffset.x * FlxG._DeviceScaleFactorX
			End If

			If (_fxShakeOffset.y <> 0) Then
				_xnaRectHelper.y += _fxShakeOffset.y * FlxG._DeviceScaleFactorY
			End If

			If (_xnaRectHelper.x < 0) Then
				_xnaRectHelper.width += _xnaRectHelper.x
				_xnaRectHelper.x = 0
			End If

			If (_xnaRectHelper.y < 0) Then
				_xnaRectHelper.height += _xnaRectHelper.y
				_xnaRectHelper.y = 0
			End If

			If (_xnaRectHelper.Right > FlxG.DeviceWidth) Then
				_xnaRectHelper.width -= _xnaRectHelper.Right - FlxG.DeviceWidth
			End If

			If (_xnaRectHelper.Bottom > FlxG.DeviceHeight) Then
				_xnaRectHelper.height -= _xnaRectHelper.Bottom - FlxG.DeviceHeight
			End If

			If (_xnaRectHelper.width <= 0 And _xnaRectHelper.height <= 0) Then
				SetScissor(_xnaRectHelper.x, _xnaRectHelper.y, _xnaRectHelper.width, _xnaRectHelper.height)
			End If
		#Else
			If (_fxShakeOffset.x <> 0 Or _fxShakeOffset.y <> 0) Then
				SetScissor(_realX + _fxShakeOffset.x * FlxG._DeviceScaleFactorX, _realY + _fxShakeOffset.y * FlxG._DeviceScaleFactorY, _realWidth, _realHeight)
			Else
				SetScissor(_realX, _realY, _realWidth, _realHeight)
			End If
		#End
		End If

		PushMatrix()

		Translate(_x + _fxShakeOffset.x, _y + _fxShakeOffset.y)
		Scale(_scaleX, _scaleY)

		If (_clipped Or _bgColor.argb <> FlxG._BgColor.argb)
			SetColor(_bgColor.r, _bgColor.g, _bgColor.b)
			DrawRect(0, 0, _width, _height)
			FlxG._LastDrawingColor = _bgColor.argb
		End If
	End Method

	Method Unlock:Void()
		If (_fill.argb <> 0) Then
			If (_fill.argb <> FlxG._LastDrawingColor) Then
				SetColor(_fill.r, _fill.g, _fill.b)
				FlxG._LastDrawingColor = _fill.argb
			End if

			If (FlxG._LastDrawingAlpha <> _fill.a) Then
				SetAlpha(_fill.a)
				FlxG._LastDrawingAlpha = _fill.a
			End If

			DrawRect(0, 0, _width, _height)
		End If

		PopMatrix()
		_fill.SetARGB(0)

		If (_clipped) Then
			SetScissor(0, 0, FlxG.DeviceWidth, FlxG.DeviceHeight)
		End If
	End Method

	Method Update:Void()
		If (target <> Null) Then
			If (deadzone = Null) Then
				FocusOn(target.GetMidpoint(_point))
			Else
				Local edge:Float
				Local targetX:Float
				Local targetY:Float

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

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

				edge = targetX - deadzone.x
				If (scroll.x > edge) scroll.x = edge
				edge = targetX + target.width - deadzone.x - deadzone.width
				If (scroll.x < edge) scroll.x = edge

				edge  = targetY - deadzone.y
				If (scroll.y > edge) scroll.y = edge
				edge = targetY + target.height - deadzone.y - deadzone.height
				If (scroll.y < edge) scroll.y = edge
			End If
		End If

		If (bounds <> Null) Then
			If (scroll.x < bounds.Left) scroll.x = bounds.Left
			If (scroll.x > bounds.Right - _width) scroll.x = bounds.Right - _width
			If (scroll.y < bounds.Top) scroll.y = bounds.Top
			If (scroll.y > bounds.Bottom - _height) scroll.y = bounds.Bottom - _height
		End If

		If (_fxFlashAlpha > 0) Then
			_fxFlashAlpha -= FlxG.Elapsed / _fxFlashDuration

			If (_fxFlashAlpha <= 0 And _fxFlashComplete <> Null) Then
				_fxFlashComplete.OnFlashComplete()
				_fxFlashComplete = Null
			End If
		End If

		If (_fxFadeAlpha > 0 And _fxFadeAlpha < 1) Then
			_fxFadeAlpha += FlxG.Elapsed / _fxFadeDuration

			If (_fxFadeAlpha >= 1) Then
				_fxFadeAlpha = 1
				If (_fxFadeComplete <> Null) Then
					_fxFadeComplete.OnFadeComplete()
					_fxFadeComplete = Null
				End If
			End If
		End If

		If (_fxShakeDuration > 0) Then
			_fxShakeDuration -= FlxG.Elapsed
			If (_fxShakeDuration <= 0) Then
				_fxShakeOffset.Make()

				If (_fxShakeComplete <> Null) Then
					_fxShakeComplete.OnShakeComplete()
					_fxShakeComplete = Null
				End If
			Else
				If (_fxShakeDirection = SHAKE_BOTH_AXES Or _fxShakeDirection = SHAKE_HORIZONTAL_ONLY) Then
					_fxShakeOffset.x = (FlxG.Random() * _fxShakeIntensity * _width * 2 - _fxShakeIntensity * _width) * _zoom
				End If

				If (_fxShakeDirection = SHAKE_BOTH_AXES Or _fxShakeDirection = SHAKE_VERTICAL_ONLY) Then
					_fxShakeOffset.y = (FlxG.Random() * _fxShakeIntensity * _height * 2 - _fxShakeIntensity * _height) * _zoom
				End If
			End If
		End If
	End Method

	Method Follow:Void(target:FlxObject, style:Int = STYLE_LOCKON)
		Self.target = target
		Local helper:Float

		Select (style)
			Case STYLE_PLATFORMER
				Local w:Float = _width / 8
				Local h:Float = _height / 3
				deadzone = New FlxRect((_width - w) / 2, (_height - h) / 2 - h * .25, w, h)

			Case STYLE_TOPDOWN
				helper = Max(_width, _height) / 4
				deadzone = New FlxRect((_width - helper) / 2, (_height - helper) / 2, helper, helper)

			Case STYLE_TOPDOWN_TIGHT
				helper = Max(_width, _height) / 8
				deadzone = New FlxRect((_width - helper) / 2, (_height - helper) / 2, helper, helper)

			Case STYLE_LOCKON
				deadzone = Null

			Default
				deadzone = Null
		End Select
	End Method

	Method FocusOn:Void(point:FlxPoint)
		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

		scroll.Make(point.x - _width * .5, point.y - _height * .5)
	End Method

	Method SetBounds:Void(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0, updateWorld:Bool = False)
		If (bounds = Null) bounds = New FlxRect()
		bounds.Make(x, y, width, height)
		If (updateWorld) FlxG.WorldBounds.CopyFrom(bounds)
		Update()
	End Method

	Method Flash:Void(color:Int = FlxG.WHITE, duration:Float = 1, onComplete:FlxCameraFlashListener = Null, force:Bool = False)
		If (Not force And _fxFlashAlpha > 0) Return

		_fxFlashColor = color
		If (duration <= 0) duration = _MIN_FLOAT_VALUE
		_fxFlashDuration = duration
		_fxFlashComplete = onComplete
		_fxFlashAlpha = 1
	End Method

	Method Fade:Void(color:Int = FlxG.BLACK, duration:Float = 1, onComplete:FlxCameraFadeListener = Null, force:Bool = False)
		If (Not force And _fxFadeAlpha > 0)	Return

		_fxFadeColor = color
		If (duration <= 0) duration = _MIN_FLOAT_VALUE
		_fxFadeDuration = duration
		_fxFadeComplete = onComplete
		_fxFadeAlpha = _MIN_FLOAT_VALUE
	End Method

	Method Shake:Void(intensity:Float = 0.05, duration:Float = 0.5, onComplete:FlxCameraShakeListener = Null, force:Bool = True, direction:Int = SHAKE_BOTH_AXES)
		If (Not force And (_fxShakeOffset.x <> 0 Or _fxShakeOffset.y <> 0)) Return

		_fxShakeIntensity = intensity
		_fxShakeDuration = duration
		_fxShakeComplete = onComplete
		_fxShakeDirection = direction
		_fxShakeOffset.Make()
	End Method

	Method StopFX:Void()
		_fxFlashAlpha = 0
		_fxFadeAlpha = 0
		_fxShakeDuration = 0
		_fxShakeOffset.x = 0
		_fxShakeOffset.y = 0
	End Method

	Method CopyFrom:FlxCamera(camera:FlxCamera)
		If (camera.bounds = Null) Then
			bounds = camera.bounds
		Else
			If (bounds = Null) bounds = New FlxRect()
			bounds.CopyFrom(camera.bounds)
		End If

		target = camera.target
		If (target <> Null) Then
			If (camera.deadzone = Null) Then
				deadzone = Null
			Else
				If (deadzone = Null) deadzone = New FlxRect()
				deadzone.CopyFrom(camera.deadzone)
			End If
		End If

		Return Self
	End Method

	Method X:Float() Property
		Return _x
	End Method

	Method X:Void(x:Float) Property
		_x = x
		_realX = _x * FlxG._DeviceScaleFactorX

		_clipped = _IsClipped()
	End Method

	Method Y:Float() Property
		Return _y
	End Method

	Method Y:Void(y:Float) Property
		_y = y
		_realY = _y * FlxG._DeviceScaleFactorY

		_clipped = _IsClipped()
	End Method

	Method Width:Float() Property
		Return _width
	End Method

	Method Width:Void(width:Float) Property
		_width = width
		_realWidth = Min(Float(FlxG.DeviceWidth), Floor(_width * _scaleX * FlxG._DeviceScaleFactorX))

		_clipped = _IsClipped()
	End Method

	Method Height:Float() Property
		Return _height
	End Method

	Method Height:Void(height:Float) Property
		_height = height
		_realHeight = Min(Float(FlxG.DeviceHeight), Floor(_height * _scaleY * FlxG._DeviceScaleFactorY))

		_clipped = _IsClipped()
	End Method

	Method Zoom:Float() Property
		Return _zoom
	End Method

	Method Zoom:Void(zoom:Float) Property
		If (zoom = 0) Then
			_zoom = FlxCamera.DefaultZoom
		Else
			_zoom = zoom
		End If

		SetScale(_zoom, _zoom)
	End Method

	Method Alpha:Float() Property
		Return _alpha
	End Method

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

	Method Angle:Float() Property
		Return _angle
	End Method

	Method Angle:Void(angle:Float) Property
		_angle = angle
	End Method

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

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

	Method BgColor:Int() Property
		Return _bgColor.argb
	End Method

	Method BgColor:Void(color:Int) Property
		_bgColor.SetARGB(color)
	End Method

	Method GetScale:FlxPoint()
		Return _point.Make(_scaleX, _scaleY)
	End Method

	Method SetScale:Void(x:Float, y:Float)
		_scaleX = x
		_scaleY = y
		_realWidth = Min(Float(FlxG.DeviceWidth), Floor(_width * _scaleX * FlxG._DeviceScaleFactorX))
		_realHeight = Min(Float(FlxG.DeviceHeight), Floor(_height * _scaleY * FlxG._DeviceScaleFactorY))
	End Method

	Method Fill:Void(color:Int, blendAlpha:Bool = True)
		If (blendAlpha) Then
			_fill.SetARGB(color)
		Else
			_fill.SetRGB(color)
		End If
	End Method

	Method DrawFX:Void()
		If (_fxFlashAlpha > 0) Then
			Local alphaComponent:Float

			alphaComponent = _fxFlashColor Shr 24
			If (alphaComponent <= 0) alphaComponent = $FF
			Fill(((alphaComponent * _fxFlashAlpha) Shl 24) + (_fxFlashColor & $00FFFFFF))
		End If

		If (_fxFadeAlpha > 0) Then
			Local alphaComponent:Float

			alphaComponent = _fxFadeColor Shr 24
			If (alphaComponent <= 0) alphaComponent = $FF
			Fill(((alphaComponent * _fxFadeAlpha) Shl 24) + (_fxFadeColor & $00FFFFFF))
		End If
	End Method

	Method ID:Int() Property
		Return _id
	End Method

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

Private
	Method _IsClipped:Bool()
		Return _realX <> 0 Or _realY <> 0 Or _realWidth <> FlxG.DeviceWidth Or _realHeight <> FlxG.DeviceHeight
	End Method

End Class

Interface FlxCameraFlashListener

	Method OnFlashComplete:Void()

End Interface

Interface FlxCameraFadeListener

	Method OnFadeComplete:Void()

End Interface

Interface FlxCameraShakeListener

	Method OnShakeComplete:Void()

End Interface

Private
Class FlxCameraClass Implements FlxClass

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

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

End Class

Strict

Import flxextern
Import flxpoint
Import flxrect
Import flxbasic
Import flxobject
Import flxg

Import system.flxcolor

Class FlxCamera Extends FlxBasic

	Global ClassObject:FlxClass = New FlxCameraClass()

	Const STYLE_LOCKON:Int = 0

	Const STYLE_PLATFORMER:Int = 1

	Const STYLE_TOPDOWN:Int = 2

	Const STYLE_TOPDOWN_TIGHT:Int = 3

	Const SHAKE_BOTH_AXES:Int = 0

	Const SHAKE_HORIZONTAL_ONLY:Int = 1

	Const SHAKE_VERTICAL_ONLY:Int  = 2

	Global DefaultZoom:Float

	Field target:FlxObject

	Field deadzone:FlxRect

	Field bounds:FlxRect

	Field scroll:FlxPoint

	Field _color:FlxColor

Private
	Const _MIN_FLOAT_VALUE:Float = .0000001

	Field _x:Float

	Field _y:Float

	Field _realX:Float

	Field _realY:Float

	Field _width:Float

	Field _height:Float

	Field _realWidth:Float

	Field _realHeight:Float

	Field _zoom:Float

	Field _point:FlxPoint

	Field _alpha:Float

	Field _scaleX:Float

	Field _scaleY:Float

	Field _angle:Float

	Field _clipped:Bool

	Field _bgColor:FlxColor

	Field _fxFlashColor:Int

	Field _fxFlashDuration:Float

	Field _fxFlashComplete:FlxCameraFlashListener

	Field _fxFlashAlpha:Float

	Field _fxFadeColor:Int

	Field _fxFadeDuration:Float

	Field _fxFadeComplete:FlxCameraFadeListener

	Field _fxFadeAlpha:Float

	Field _fxShakeIntensity:Float

	Field _fxShakeDuration:Float

	Field _fxShakeComplete:FlxCameraShakeListener

	Field _fxShakeOffset:FlxPoint

	Field _fxShakeDirection:Int

	Field _fill:FlxColor

	Field _id:Int

	Global _Inkrement:Int

Public
	Method New(x:Int, y:Int, width:Int, height:Int, zoom:Float = 0)
		Zoom = zoom 'must be first for init
		X = x
		Y = y
		Width = width
		Height = height
		target = Null
		deadzone = Null
		scroll = New FlxPoint()
		_point = New FlxPoint()
		bounds = Null
		_bgColor = New FlxColor(FlxG.BgColor())
		_color = New FlxColor()
		_alpha = 1

		_fxFlashColor = 0
		_fxFlashDuration = 0
		_fxFlashComplete = null
		_fxFlashAlpha = 0

		_fxFadeColor = 0
		_fxFadeDuration = 0
		_fxFadeComplete = Null
		_fxFadeAlpha = 0

		_fxShakeIntensity = 0
		_fxShakeDuration = 0
		_fxShakeComplete = Null
		_fxShakeOffset = New FlxPoint()
		_fxShakeDirection = 0

		active = True

		_fill = New FlxColor(0)

		_id = _Inkrement
		_Inkrement += 1
	End Method

	Method Destroy:Void()
		target = Null
		scroll = Null
		deadzone = Null
		bounds = Null
		_color = Null
		_bgColor = Null
		_fxFlashComplete = Null
		_fxFadeComplete = Null
		_fxShakeComplete = Null
		_fxShakeOffset = Null
		_fill = Null
	End Method

	Method Lock:Void()
		If (_clipped) Then
			SetScissor(_realX + _fxShakeOffset.x * FlxG._DeviceScaleFactorX, _realY + _fxShakeOffset.y * FlxG._DeviceScaleFactorY, _realWidth, _realHeight)
		End If

		PushMatrix()

		Translate(_x + _fxShakeOffset.x, _y + _fxShakeOffset.y)
		Scale(_scaleX, _scaleY)

		If (_clipped Or _bgColor.argb <> FlxG._BgColor.argb)
			SetColor(_bgColor.r, _bgColor.g, _bgColor.b)
			DrawRect(0, 0, _width, _height)
			FlxG._LastDrawingColor = _bgColor.argb
		End If
	End Method

	Method Unlock:Void()
		If (_fill.argb <> 0) Then
			If (_fill.argb <> FlxG._LastDrawingColor) Then
				SetColor(_fill.r, _fill.g, _fill.b)
				FlxG._LastDrawingColor = _fill.argb
			End if

			If (FlxG._LastDrawingAlpha <> _fill.a) Then
				SetAlpha(_fill.a)
				FlxG._LastDrawingAlpha = _fill.a
			End If

			DrawRect(0, 0, _width, _height)
		End If

		PopMatrix()
		_fill.SetARGB(0)

		If (_clipped) Then
			SetScissor(0, 0, FlxG.DeviceWidth, FlxG.DeviceHeight)
		End If
	End Method

	Method Update:Void()
		If (target <> Null) Then
			If (deadzone = Null) Then
				FocusOn(target.GetMidpoint(_point))
			Else
				Local edge:Float
				Local targetX:Float
				Local targetY:Float

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

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

				edge = targetX - deadzone.x
				If (scroll.x > edge) scroll.x = edge
				edge = targetX + target.width - deadzone.x - deadzone.width
				If (scroll.x < edge) scroll.x = edge

				edge  = targetY - deadzone.y
				If (scroll.y > edge) scroll.y = edge
				edge = targetY + target.height - deadzone.y - deadzone.height
				If (scroll.y < edge) scroll.y = edge
			End If
		End If

		If (bounds <> Null) Then
			If (scroll.x < bounds.Left) scroll.x = bounds.Left
			If (scroll.x > bounds.Right - _width) scroll.x = bounds.Right - _width
			If (scroll.y < bounds.Top) scroll.y = bounds.Top
			If (scroll.y > bounds.Bottom - _height) scroll.y = bounds.Bottom - _height
		End If

		If (_fxFlashAlpha > 0) Then
			_fxFlashAlpha -= FlxG.Elapsed / _fxFlashDuration

			If (_fxFlashAlpha <= 0 And _fxFlashComplete <> Null) Then
				_fxFlashComplete.OnFlashComplete()
				_fxFlashComplete = Null
			End If
		End If

		If (_fxFadeAlpha > 0 And _fxFadeAlpha < 1) Then
			_fxFadeAlpha += FlxG.Elapsed / _fxFadeDuration

			If (_fxFadeAlpha >= 1) Then
				_fxFadeAlpha = 1
				If (_fxFadeComplete <> Null) Then
					_fxFadeComplete.OnFadeComplete()
					_fxFadeComplete = Null
				End If
			End If
		End If

		If (_fxShakeDuration > 0) Then
			_fxShakeDuration -= FlxG.Elapsed
			If (_fxShakeDuration <= 0) Then
				_fxShakeOffset.Make()

				If (_fxShakeComplete <> Null) Then
					_fxShakeComplete.OnShakeComplete()
					_fxShakeComplete = Null
				End If
			Else
				If (_fxShakeDirection = SHAKE_BOTH_AXES Or _fxShakeDirection = SHAKE_HORIZONTAL_ONLY) Then
					_fxShakeOffset.x = (FlxG.Random() * _fxShakeIntensity * _width * 2 - _fxShakeIntensity * _width) * _zoom
				End If

				If (_fxShakeDirection = SHAKE_BOTH_AXES Or _fxShakeDirection = SHAKE_VERTICAL_ONLY) Then
					_fxShakeOffset.y = (FlxG.Random() * _fxShakeIntensity * _height * 2 - _fxShakeIntensity * _height) * _zoom
				End If
			End If
		End If
	End Method

	Method Follow:Void(target:FlxObject, style:Int = STYLE_LOCKON)
		Self.target = target
		Local helper:Float

		Select (style)
			Case STYLE_PLATFORMER
				Local w:Float = _width / 8
				Local h:Float = _height / 3
				deadzone = New FlxRect((_width - w) / 2, (_height - h) / 2 - h * .25, w, h)

			Case STYLE_TOPDOWN
				helper = Max(_width, _height) / 4
				deadzone = New FlxRect((_width - helper) / 2, (_height - helper) / 2, helper, helper)

			Case STYLE_TOPDOWN_TIGHT
				helper = Max(_width, _height) / 8
				deadzone = New FlxRect((_width - helper) / 2, (_height - helper) / 2, helper, helper)

			Case STYLE_LOCKON
				deadzone = Null

			Default
				deadzone = Null
		End Select
	End Method

	Method FocusOn:Void(point:FlxPoint)
		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

		scroll.Make(point.x - _width * .5, point.y - _height * .5)
	End Method

	Method SetBounds:Void(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0, updateWorld:Bool = False)
		If (bounds = Null) bounds = New FlxRect()
		bounds.Make(x, y, width, height)
		If (updateWorld) FlxG.WorldBounds.CopyFrom(bounds)
		Update()
	End Method

	Method Flash:Void(color:Int = FlxG.WHITE, duration:Float = 1, onComplete:FlxCameraFlashListener = Null, force:Bool = False)
		If (Not force And _fxFlashAlpha > 0) Return

		_fxFlashColor = color
		If (duration <= 0) duration = _MIN_FLOAT_VALUE
		_fxFlashDuration = duration
		_fxFlashComplete = onComplete
		_fxFlashAlpha = 1
	End Method

	Method Fade:Void(color:Int = FlxG.BLACK, duration:Float = 1, onComplete:FlxCameraFadeListener = Null, force:Bool = False)
		If (Not force And _fxFadeAlpha > 0)	Return

		_fxFadeColor = color
		If (duration <= 0) duration = _MIN_FLOAT_VALUE
		_fxFadeDuration = duration
		_fxFadeComplete = onComplete
		_fxFadeAlpha = _MIN_FLOAT_VALUE
	End Method

	Method Shake:Void(intensity:Float = 0.05, duration:Float = 0.5, onComplete:FlxCameraShakeListener = Null, force:Bool = True, direction:Int = SHAKE_BOTH_AXES)
		If (Not force And (_fxShakeOffset.x <> 0 Or _fxShakeOffset.y <> 0)) Return

		_fxShakeIntensity = intensity
		_fxShakeDuration = duration
		_fxShakeComplete = onComplete
		_fxShakeDirection = direction
		_fxShakeOffset.Make()
	End Method

	Method StopFX:Void()
		_fxFlashAlpha = 0
		_fxFadeAlpha = 0
		_fxShakeDuration = 0
		_fxShakeOffset.x = 0
		_fxShakeOffset.y = 0
	End Method

	Method CopyFrom:FlxCamera(camera:FlxCamera)
		If (camera.bounds = Null) Then
			bounds = camera.bounds
		Else
			If (bounds = Null) bounds = New FlxRect()
			bounds.CopyFrom(camera.bounds)
		End If

		target = camera.target
		If (target <> Null) Then
			If (camera.deadzone = Null) Then
				deadzone = Null
			Else
				If (deadzone = Null) deadzone = New FlxRect()
				deadzone.CopyFrom(camera.deadzone)
			End If
		End If

		Return Self
	End Method

	Method X:Float() Property
		Return _x
	End Method

	Method X:Void(x:Float) Property
		_x = x
		_realX = _x * FlxG._DeviceScaleFactorX

		_clipped = _IsClipped()
	End Method

	Method Y:Float() Property
		Return _y
	End Method

	Method Y:Void(y:Float) Property
		_y = y
		_realY = _y * FlxG._DeviceScaleFactorY

		_clipped = _IsClipped()
	End Method

	Method Width:Float() Property
		Return _width
	End Method

	Method Width:Void(width:Float) Property
		_width = width
		_realWidth = Min(Float(FlxG.DeviceWidth), Floor(_width * _scaleX * FlxG._DeviceScaleFactorX))

		_clipped = _IsClipped()
	End Method

	Method Height:Float() Property
		Return _height
	End Method

	Method Height:Void(height:Float) Property
		_height = height
		_realHeight = Min(Float(FlxG.DeviceHeight), Floor(_height * _scaleY * FlxG._DeviceScaleFactorY))

		_clipped = _IsClipped()
	End Method

	Method Zoom:Float() Property
		Return _zoom
	End Method

	Method Zoom:Void(zoom:Float) Property
		If (zoom = 0) Then
			_zoom = FlxCamera.DefaultZoom
		Else
			_zoom = zoom
		End If

		SetScale(_zoom, _zoom)
	End Method

	Method Alpha:Float() Property
		Return _alpha
	End Method

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

	Method Angle:Float() Property
		Return _angle
	End Method

	Method Angle:Void(angle:Float) Property
		_angle = angle
	End Method

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

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

	Method BgColor:Int() Property
		Return _bgColor.argb
	End Method

	Method BgColor:Void(color:Int) Property
		_bgColor.SetARGB(color)
	End Method

	Method GetScale:FlxPoint()
		Return _point.Make(_scaleX, _scaleY)
	End Method

	Method SetScale:Void(x:Float, y:Float)
		_scaleX = x
		_scaleY = y
		_realWidth = Min(Float(FlxG.DeviceWidth), Floor(_width * _scaleX * FlxG._DeviceScaleFactorX))
		_realHeight = Min(Float(FlxG.DeviceHeight), Floor(_height * _scaleY * FlxG._DeviceScaleFactorY))
	End Method

	Method Fill:Void(color:Int, blendAlpha:Bool = True)
		If (blendAlpha) Then
			_fill.SetARGB(color)
		Else
			_fill.SetRGB(color)
		End If
	End Method

	Method DrawFX:Void()
		If (_fxFlashAlpha > 0) Then
			Local alphaComponent:Float

			alphaComponent = _fxFlashColor Shr 24
			If (alphaComponent <= 0) alphaComponent = $FF
			Fill(((alphaComponent * _fxFlashAlpha) Shl 24) + (_fxFlashColor & $00FFFFFF))
		End If

		If (_fxFadeAlpha > 0) Then
			Local alphaComponent:Float

			alphaComponent = _fxFadeColor Shr 24
			If (alphaComponent <= 0) alphaComponent = $FF
			Fill(((alphaComponent * _fxFadeAlpha) Shl 24) + (_fxFadeColor & $00FFFFFF))
		End If
	End Method

	Method ID:Int() Property
		Return _id
	End Method

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

Private
	Method _IsClipped:Bool()
		Return _realX <> 0 Or _realY <> 0 Or _realWidth <> FlxG.DeviceWidth Or _realHeight <> FlxG.DeviceHeight
	End Method

End Class

Interface FlxCameraFlashListener

	Method OnFlashComplete:Void()

End Interface

Interface FlxCameraFadeListener

	Method OnFadeComplete:Void()

End Interface

Interface FlxCameraShakeListener

	Method OnShakeComplete:Void()

End Interface

Private
Class FlxCameraClass Implements FlxClass

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

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

End Class