Fixed FlxTileblock creation bug

Fixed FlxTileblock creation bug

Strict

Import mojo

Import flxsprite
Import flxg

Class FlxTileblock Extends FlxSprite Implements FlxSpriteRenderer

	Global ClassObject:Object

Private
	Field _tilePixels:Image

	Field _stamps:Stack<FlxStamp>

Public
	Method New(x:Int, y:Int, width:Int, height:Int, makeGraphic:Bool = False)
		Super.New(x, y)

		SetRenderer(Self)

		If (makeGraphic) Then
			MakeGraphic(width, height, 0)
		Else
			Pixels = Null

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

			_ResetHelpers()
		End If

		active = False
		immovable = True
		moves = False

		_stamps = New Stack<FlxStamp>()
	End Method

	Method Destroy:Void()
		If (_tilePixels <> Null) Then
			_tilePixels = Null
		End If

		If (_stamps.Length() > 0) Then
			Local i:Int = 0
			Local l:Int = _stamps.Length()
			Local stamp:FlxStamp

			While (i < l)
				_stamps.Set(i, Null)
				i += 1
			Wend

			_stamps.Clear()
		End If

		Super.Destroy()
	End Method

	Method LoadTiles:FlxTileblock(tileGraphic:String, tileWidth:Int = 0, tileHeight:Int = 0, empties:Int = 0)
		If (tileGraphic.Length() = 0) Return Self

		Local sprite:FlxSprite = (New FlxSprite()).LoadGraphic(tileGraphic, True, False, tileWidth, tileHeight)
		Local spriteWidth:Int = sprite.width
		Local spriteHeight:Int = sprite.height
		Local total:Int = sprite.frames + empties

		_tilePixels = sprite.Pixels

		If (width Mod spriteWidth <> 0) Then
			width = Int(width / spriteWidth + 1) * spriteWidth
		End If

		If (height Mod spriteHeight <> 0) Then
			height = Int(height / spriteHeight + 1) * spriteHeight
		End If

		If (_tilePixels <> Null) MakeGraphic(width, height, FlxG.WHITE)

		Local row:Int = 0
		Local column:Int
		Local destinationX:Int
		Local destinationY:Int = 0
		Local widthInTiles:Int = width / spriteWidth
		Local heightInTiles:Int = height / spriteHeight
		Local i:Int = 0

		_stamps.Clear()

		While (row < heightInTiles)
			destinationX = 0
			column = 0

			While (column < widthInTiles)
				If (FlxG.Random() * total > empties) Then
					_stamps.Push(New FlxStamp(destinationX, destinationY, Int(FlxG.Random() * (sprite.frames - 1))))
				End If

				destinationX += spriteWidth
				column += 1
			Wend

			destinationY += spriteHeight
			row += 1
		Wend

		Return Self
	End Method

	Method OnSpriteRender:Void(x:Float, y:Float)
		If (_tilePixels = Null) Return

		Local i:Int = 0
		Local l:Int = _stamps.Length()
		Local stamp:FlxStamp

		While (i < l)
			stamp = _stamps.Get(i)
			DrawImage(_tilePixels, x + stamp.x, y + stamp.y, stamp.frame)
			i += 1
		Wend
	End Method

End Class

Private
Class FlxStamp

	Field x:Int

	Field y:Int

	Field frame:Int

	Method New(x:Int, y:Int, frame:Int)
		Self.x = x
		Self.y = y
		Self.frame = frame
	End Method

End Class

Strict

Import mojo

Import flxsprite
Import flxg

Class FlxTileblock Extends FlxSprite Implements FlxSpriteRenderer

	Global ClassObject:Object

Private
	Field _tilePixels:Image

	Field _stamps:Stack<FlxStamp>

Public
	Method New(x:Int, y:Int, width:Int, height:Int, makeGraphic:Bool = False)
		Super.New(x, y)

		SetRenderer(Self)
		If (makeGraphic) MakeGraphic(width, height, 0)
		active = False
		immovable = True
		moves = False

		_stamps = New Stack<FlxStamp>()
	End Method

	Method Destroy:Void()
		If (_tilePixels <> Null) Then
			_tilePixels = Null
		End If

		If (_stamps.Length() > 0) Then
			Local i:Int = 0
			Local l:Int = _stamps.Length()
			Local stamp:FlxStamp

			While (i < l)
				_stamps.Set(i, Null)
				i += 1
			Wend

			_stamps.Clear()
		End If

		Super.Destroy()
	End Method

	Method LoadTiles:FlxTileblock(tileGraphic:String, tileWidth:Int = 0, tileHeight:Int = 0, empties:Int = 0)
		If (tileGraphic.Length() = 0) Return Self

		Local sprite:FlxSprite = (New FlxSprite()).LoadGraphic(tileGraphic, True, False, tileWidth, tileHeight)
		Local spriteWidth:Int = sprite.width
		Local spriteHeight:Int = sprite.height
		Local total:Int = sprite.frames + empties

		_tilePixels = sprite.Pixels

		If (width Mod spriteWidth <> 0) Then
			width = Int(width / spriteWidth + 1) * spriteWidth
		End If

		If (height Mod spriteHeight <> 0) Then
			height = Int(height / spriteHeight + 1) * spriteHeight
		End If

		If (_tilePixels <> Null) MakeGraphic(width, height, FlxG.WHITE)

		Local row:Int = 0
		Local column:Int
		Local destinationX:Int
		Local destinationY:Int = 0
		Local widthInTiles:Int = width / spriteWidth
		Local heightInTiles:Int = height / spriteHeight
		Local i:Int = 0

		_stamps.Clear()

		While (row < heightInTiles)
			destinationX = 0
			column = 0

			While (column < widthInTiles)
				If (FlxG.Random() * total > empties) Then
					_stamps.Push(New FlxStamp(destinationX, destinationY, Int(FlxG.Random() * (sprite.frames - 1))))
				End If

				destinationX += spriteWidth
				column += 1
			Wend

			destinationY += spriteHeight
			row += 1
		Wend

		Return Self
	End Method

	Method OnSpriteRender:Void(x:Float, y:Float)
		If (_tilePixels = Null) Return

		Local i:Int = 0
		Local l:Int = _stamps.Length()
		Local stamp:FlxStamp

		While (i < l)
			stamp = _stamps.Get(i)
			DrawImage(_tilePixels, x + stamp.x, y + stamp.y, stamp.frame)
			i += 1
		Wend
	End Method

End Class

Private
Class FlxStamp

	Field x:Int

	Field y:Int

	Field frame:Int

	Method New(x:Int, y:Int, frame:Int)
		Self.x = x
		Self.y = y
		Self.frame = frame
	End Method

End Class


Updated SpaceBugs.monkey

Updated SpaceBugs.monkey


Fixed AssertRange functions.

Fixed AssertRange functions.

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

Strict

Const ASSERT_MESSAGE$ = "Assertion failed!"

#Rem
	Assertion functions.
	Call the corresponding Assert function with the check that you expect to pass.  If it fails, the
	error message will be thrown.
#End

Function Assert:Void(val?, msg$=ASSERT_MESSAGE)
	If Not val Then AssertError(msg)
End

Function AssertNull:Void(val:Object, msg$=ASSERT_MESSAGE)
	If val <> Null Then AssertError(msg)
End

Function AssertNotNull:Void(val:Object, msg$=ASSERT_MESSAGE)
	If val = Null Then AssertError(msg)
End

Function AssertEqualsInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val <> expected Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertEqualsFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val <> expected Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertEqualsString:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	If val <> expected Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertEqualsIgnoreCase:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	' TODO: optimise this (does it create new objects?)
	If val.ToLower() <> expected.ToLower() Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertNotEqualInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val = expected Then AssertError(msg + " " + val + "="+expected)
End

Function AssertNotEqualFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val = expected Then AssertError(msg + " " + val + "="+expected)
End

Function AssertNotEqualString:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	If val = expected Then AssertError(msg + " " + val + "="+expected)
End

Function AssertNotEqualIgnoreCase:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	' TODO:
End

Function AssertLessThanInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val >= expected Then AssertError(msg + " " + val + ">="+expected)
End

Function AssertLessThanFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val >= expected Then AssertError(msg + " " + val + ">="+expected)
End

Function AssertGreaterThanInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val <= expected Then AssertError(msg + " " + val + "<="+expected)
End

Function AssertGreaterThanFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val <= expected Then AssertError(msg + " " + val + "<="+expected)
End

Function AssertLessThanOrEqualInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val > expected Then AssertError(msg + " " + val + ">"+expected)
End

Function AssertLessThanOrEqualFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val > expected Then AssertError(msg + " " + val + ">"+expected)
End

Function AssertGreaterThanOrEqualInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val < expected Then AssertError(msg + " " + val + "<"+expected)
End

Function AssertGreaterThanOrEqualFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val < expected Then AssertError(msg + " " + val + "<"+expected)
End

Function AssertRangeInt:Void(val%, minbound%, maxbound%, msg$=ASSERT_MESSAGE)
	If val < minbound Or val >= maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<" + maxbound)
End

Function AssertRangeFloat:Void(val#, minbound#, maxbound#, msg$=ASSERT_MESSAGE)
	If val < minbound Or val >= maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<" + maxbound)
End

Function AssertRangeInclusiveInt:Void(val%, minbound%, maxbound%, msg$=ASSERT_MESSAGE)
	If val < minbound Or val > maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<=" + maxbound)
End

Function AssertRangeInclusiveFloat:Void(val#, minbound#, maxbound#, msg$=ASSERT_MESSAGE)
	If val < minbound Or val > maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<=" + maxbound)
End

Function AssertError:Void(msg:String)
	Print(msg)
	Error(msg)
End

Strict

Const ASSERT_MESSAGE$ = "Assertion failed!"

#Rem
	Assertion functions.
	Call the corresponding Assert function with the check that you expect to pass.  If it fails, the
	error message will be thrown.
#End

Function Assert:Void(val?, msg$=ASSERT_MESSAGE)
	If Not val Then AssertError(msg)
End

Function AssertNull:Void(val:Object, msg$=ASSERT_MESSAGE)
	If val <> Null Then AssertError(msg)
End

Function AssertNotNull:Void(val:Object, msg$=ASSERT_MESSAGE)
	If val = Null Then AssertError(msg)
End

Function AssertEqualsInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val <> expected Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertEqualsFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val <> expected Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertEqualsString:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	If val <> expected Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertEqualsIgnoreCase:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	' TODO: optimise this (does it create new objects?)
	If val.ToLower() <> expected.ToLower() Then AssertError(msg + " " + val + "<>"+expected)
End

Function AssertNotEqualInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val = expected Then AssertError(msg + " " + val + "="+expected)
End

Function AssertNotEqualFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val = expected Then AssertError(msg + " " + val + "="+expected)
End

Function AssertNotEqualString:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	If val = expected Then AssertError(msg + " " + val + "="+expected)
End

Function AssertNotEqualIgnoreCase:Void(val$, expected$, msg$=ASSERT_MESSAGE)
	' TODO:
End

Function AssertLessThanInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val >= expected Then AssertError(msg + " " + val + ">="+expected)
End

Function AssertLessThanFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val >= expected Then AssertError(msg + " " + val + ">="+expected)
End

Function AssertGreaterThanInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val <= expected Then AssertError(msg + " " + val + "<="+expected)
End

Function AssertGreaterThanFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val <= expected Then AssertError(msg + " " + val + "<="+expected)
End

Function AssertLessThanOrEqualInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val > expected Then AssertError(msg + " " + val + ">"+expected)
End

Function AssertLessThanOrEqualFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val > expected Then AssertError(msg + " " + val + ">"+expected)
End

Function AssertGreaterThanOrEqualInt:Void(val%, expected%, msg$=ASSERT_MESSAGE)
	If val < expected Then AssertError(msg + " " + val + "<"+expected)
End

Function AssertGreaterThanOrEqualFloat:Void(val#, expected#, msg$=ASSERT_MESSAGE)
	If val < expected Then AssertError(msg + " " + val + "<"+expected)
End

Function AssertRangeInt:Void(val%, minbound%, maxbound%, msg$=ASSERT_MESSAGE)
	If val < minbound Or val >= maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<" + size)
End

Function AssertRangeFloat:Void(val#, minbound#, maxbound#, msg$=ASSERT_MESSAGE)
	If val < minbound Or val >= maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<" + size)
End

Function AssertRangeInclusiveInt:Void(val%, minbound%, maxbound%, msg$=ASSERT_MESSAGE)
	If val < minbound Or val > maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<=" + size)
End

Function AssertRangeInclusiveFloat:Void(val#, minbound#, maxbound#, msg$=ASSERT_MESSAGE)
	If val < minbound Or val > maxbound Then AssertError(msg + " " + val + " is not " + minbound + "<=val<=" + size)
End

Function AssertError:Void(msg:String)
	Print(msg)
	Error(msg)
End


Handle AssertionFailedException in TestRunner

Strict

Private

Import assertionfailedexception
Import bono.src.exceptions
Import reflection
Import testcase
Import testfailedexception
Import testincompleteexception
Import testlistener
Import testskippedexception

Public

Class TestRunner
    Private

    Field name:String
    Field classInfo:ClassInfo
    Field testMethods:Stack<MethodInfo> = New Stack<MethodInfo>()

    Public

    Method New(name:String)
        Self.name = name

        LoadClassInfo()
        ValidateClassInfo()
        ExtractMethods()
    End

    Method Run:Void(listener:TestListener)
        For Local methodInfo:MethodInfo = EachIn testMethods
            Local test:TestCase
            Try
                test = TestCase(classInfo.NewInstance())
            Catch ex:Exception
                Throw New RuntimeException("New failed")
            End

            Try
                test.SetUp()
            Catch ex:Exception
                Throw New RuntimeException("SetUp failed")
            End

            listener.StartTest(classInfo, methodInfo)
            Try
                methodInfo.Invoke(test, [])
            Catch ex:TestFailedException
                listener.AddFailure(classInfo, methodInfo, ex.ToString())
            Catch ex:AssertionFailedException
                listener.AddFailure(classInfo, methodInfo, ex.ToString())
            Catch ex:TestSkippedException
                listener.AddSkippedTest(classInfo, methodInfo, ex.ToString())
            Catch ex:TestIncompleteException
                listener.AddIncompleteTest(classInfo, methodInfo, ex.ToString())
            End
            listener.EndTest(classInfo, methodInfo)

            Try
                test.TearDown()
            Catch ex:Exception
                Throw New RuntimeException("TearDown failed")
            End
        End
    End

    Private

    Method LoadClassInfo:Void()
        classInfo = GetClass(name)
        If classInfo Then Return

        Throw New NotFoundException("No class named >" + name + "< found.")
    End

    Method ValidateClassInfo:Void()
        If classInfo.ExtendsClass(GetClass("TestCase")) Then Return

        Local msg:String = "Class >" + name + "< must inherit from TestCase."
        Throw New InvalidArgumentException(msg)
    End

    Method ExtractMethods:Void()
        For Local methodInfo:MethodInfo = EachIn classInfo.GetMethods(True)
            If methodInfo.Name().StartsWith("Test")
                testMethods.Push(methodInfo)
            End
        End
    End
End

Strict

Private

Import assertionfailedexception
Import bono.src.exceptions
Import reflection
Import testcase
Import testfailedexception
Import testincompleteexception
Import testlistener
Import testskippedexception

Public

Class TestRunner
    Private

    Field name:String
    Field classInfo:ClassInfo
    Field testMethods:Stack<MethodInfo> = New Stack<MethodInfo>()

    Public

    Method New(name:String)
        Self.name = name

        LoadClassInfo()
        ValidateClassInfo()
        ExtractMethods()
    End

    Method Run:Void(listener:TestListener)
        For Local methodInfo:MethodInfo = EachIn testMethods
            Local test:TestCase
            Try
                test = TestCase(classInfo.NewInstance())
            Catch ex:Exception
                Throw New RuntimeException("New failed")
            End

            Try
                test.SetUp()
            Catch ex:Exception
                Throw New RuntimeException("SetUp failed")
            End

            listener.StartTest(classInfo, methodInfo)
            Try
                methodInfo.Invoke(test, [])
            Catch ex:AssertionFailedException
            Catch ex:TestFailedException
                listener.AddFailure(classInfo, methodInfo, ex.ToString())
            Catch ex:TestSkippedException
                listener.AddSkippedTest(classInfo, methodInfo, ex.ToString())
            Catch ex:TestIncompleteException
                listener.AddIncompleteTest(classInfo, methodInfo, ex.ToString())
            End
            listener.EndTest(classInfo, methodInfo)

            Try
                test.TearDown()
            Catch ex:Exception
                Throw New RuntimeException("TearDown failed")
            End
        End
    End

    Private

    Method LoadClassInfo:Void()
        classInfo = GetClass(name)
        If classInfo Then Return

        Throw New NotFoundException("No class named >" + name + "< found.")
    End

    Method ValidateClassInfo:Void()
        If classInfo.ExtendsClass(GetClass("TestCase")) Then Return

        Local msg:String = "Class >" + name + "< must inherit from TestCase."
        Throw New InvalidArgumentException(msg)
    End

    Method ExtractMethods:Void()
        For Local methodInfo:MethodInfo = EachIn classInfo.GetMethods(True)
            If methodInfo.Name().StartsWith("Test")
                testMethods.Push(methodInfo)
            End
        End
    End
End


Added 'PublicDataStream.CloseWithoutBuffer' and co.

Strict

Public

' Preprocessor related:
' Nothing so far.

' Imports (Public):
Import regal.byteorder

' BRL:
Import brl.stream

' Imports (Private):
Private

Import regal.util

' BRL:
Import brl.databuffer

Public

' Classes:
Class PublicDataStream Extends Stream Implements IOnLoadDataComplete
	' Constant variable(s):
	Const NOLIMIT:Int = 0 ' -1

	' Defaults:
	Const Default_ResizeScalar:Float = 1.5 ' 2.0

	' Booleans / Flags:
	Const Default_BigEndianStorage:Bool = False

	' Constructor(s) (Public):
	Method New(Size:Int, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		GenerateBuffer(Size)

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(B:DataBuffer, Length:Int, Offset:Int=0, Copy:Bool=False, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		If (Copy) Then
			GenerateBuffer(B)

			OwnsBuffer = True
		Else
			Self.Data = B
		Endif

		Self.Offset = Offset
		Self._Length = Length

		Self.RawSize = Length

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(Path:String, BigEndianStorage:Bool=Default_BigEndianStorage, Async:Bool=False)
		If (Not Async) Then
			OnLoadDataComplete(DataBuffer.Load(Path), Path)
		Else
			DataBuffer.LoadAsync(Path, Self)
		Endif

		Self.BigEndianStorage = BigEndianStorage
	End

	' Constructor(s) (Protected):
	Protected

	Method GenerateBuffer:Void(Size:Int)
		Self.Data = New DataBuffer(Size)
		Self.RawSize = Size
		Self.OwnsBuffer = True

		Return
	End

	' This will copy the contents of 'B' into an internally managed buffer.
	Method GenerateBuffer:Void(B:DataBuffer)
		GenerateBuffer(B.Length)

		B.CopyBytes(0, Data, 0, Data.Length)

		Return
	End

	Public

	' Destructor(s) (Protected):
	Protected

	Method DestructDetails:Void()
		Self.Offset = 0
		Self._Position = 0

		Return
	End

	Public

	' Destructor(s) (Public):
	Method FreeBuffer:Void(DiscardBuffer:Bool=True)
		If (Data <> Null) Then
			If (DiscardBuffer) Then
				Data.Discard()
			Endif

			Data = Null
		Endif

		OwnsBuffer = False

		RawSize = 0

		Return
	End

	Method Close:Void()
		FreeBuffer(OwnsBuffer)
		DestructDetails()

		Return
	End

	' The resulting 'DataBuffer' represents the 'Data'
	' property before destruction takes place.
	Method CloseWithoutBuffer:DataBuffer()
		Local OutputBuffer:= Data

		FreeBuffer(False)
		DestructDetails()

		Return OutputBuffer
	End

	' Methods:
	Method Seek:Int(Input:Int=0)
		Self._Position = Clamp(Input, 0, DataLength)

		Return Self._Position
	End

	Method Reset:Void()
		Seek()
		ResetLength()

		Return
	End

	' This states if the number of bytes specified may be read safely.
	Method WillOverReach:Bool(Bytes:Int)
		Return ReadWillOverReach(Bytes)
	End

	Method ReadWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > Length)
	End

	Method WriteWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > DataLength) ' Length
	End

	Method ReadShort:Int()
		If (BigEndianStorage) Then
			Return NToHS(Super.ReadShort())
		Endif

		Return Super.ReadShort()
	End

	Method ReadInt:Int()
		If (BigEndianStorage) Then
			Return NToHL(Super.ReadInt())
		Endif

		Return Super.ReadInt()
	End

	Method ReadFloat:Float()
		If (BigEndianStorage) Then
			Return NToHF(Super.ReadInt())
		Endif

		Return Super.ReadFloat()
	End

	Method WriteShort:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteShort(HToNS(Value))
		Else
			Super.WriteShort(Value)
		Endif

		Return
	End

	Method WriteInt:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNL(Value))
		Else
			Super.WriteInt(Value)
		Endif

		Return
	End

	Method WriteFloat:Void(Value:Float)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNF(Value))
		Else
			Super.WriteFloat(Value)
		Endif

		Return
	End

	Method Read:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		If (WillOverReach(Count)) Then
			Count = Self.BytesLeft
		Endif

		#Rem
			For Local Index:= 0 Until Count
				Buf.PokeByte(Offset+Index, Self.Data.PeekByte(Offset+Position+Index))
			Next
		#End

		Self.Data.CopyBytes(Self.DataOffset, Buf, Offset, Count)

		Self._Position += Count

		Return Count
	End

	Method Write:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		Local NewPosition:= (Count+Position)

		If (NewPosition > DataLength) Then
			If (ShouldResize) Then
				AutoResize(Count)
			Else
				Count = (DataLength-Position) ' Max(..., 0)
			Endif
		Endif

		Buf.CopyBytes(Offset, Self.Data, Self.DataOffset, Count)

		Self._Position = NewPosition

		SetLengthSafe(NewPosition)

		Return Count
	End

	' This transfers a raw segment of the internal buffer into 'S'. (Use at your own risk)
	Method TransferSegment:Void(S:Stream, Bytes:Int, Offset:Int)
		S.WriteAll(Data, Offset, Bytes)

		Return
	End

	' This may be used to transfer the internal data of this stream to another.
	Method TransferTo:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, Length, ReadOffset)

		Return
	End

	' This may be used to transfer what has already been read.
	Method TransferPastData:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, (Position-ReadOffset), ReadOffset)

		Return
	End

	' This may be used to read the number of bytes specified, from this stream then transfer it to another.
	Method TransferAmount:Void(S:Stream, Bytes:Int, Offset:Int=0)
		Local P:= S.Position

		TransferSegment(S, Bytes, (Self.Offset+Self.Position+Offset))

		S.Seek(P)

		Return
	End

	Method AutoResize:Bool(MinBytes:Int=0)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		Return Resize(Max(Int(Float(Data.Length) * ResizeScalar), MinBytes))
	End

	Method SmartResize:Bool(MinBytes:Int)
		If (MinBytes <= DataLength) Then
			Return True
		Endif

		Return Resize(MinBytes)
	End

	Method Resize:Bool(NewSize:Int, Force:Bool=False)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		If (SizeLimit <> NOLIMIT) Then
			If (NewSize > SizeLimit) Then
				If (Force) Then
					NewSize = Min(NewSize, SizeLimit)
				Else
					Return False
				Endif
			Endif
		Endif

		Data = ResizeBuffer(Data, NewSize, True, True, True)

		Return (Data <> Null)
	End

	Method SetLength:Void(Value:Int)
		Self._Length = Min(Value, DataLength)

		Return
	End

	Method SetLengthSafe:Void(Value:Int)
		SetLength(Max(Value, Self._Length))

		Return
	End

	' This sets the internal length to the current position.
	Method ClampToPosition:Void()
		SetLength(Position)

		Return
	End

	Method ResetLength:Void()
		SetLength(0)

		Return
	End

	' Call-backs:
	Method OnLoadDataComplete:Void(Data:DataBuffer, Path:String)
		If (Self.Data = Null) Then
			Self.Data = Data
			Self._Length = Data.Length
		Endif

		Return
	End

	' Properties (Public):

	' The internal I/O buffer.
	Method Data:DataBuffer() Property
		Return Self._Data
	End

	Method Eof:Int() Property
		Return (Position >= Length)
	End

	' The furthest this stream has written.
	Method Length:Int() Property
		Return Self._Length
	End

	' The number of bytes into the buffer this stream is.
	Method Position:Int() Property
		Return Self._Position
	End

	' The overall length of the 'Data' buffer. (Taking 'Offset' into account)
	Method RawSize:Int() Property
		Return Self._RawSize
	End

	' This will return the 'RawSize' property adjusted to the internal offset.
	Method DataLength:Int() Property
		Return (RawSize - Offset)
	End

	' The real position in the 'Data' buffer. (After 'Offset')
	Method DataOffset:Int() Property
		Return (Offset+Position)
	End

	' The number of bytes left in 'Data'. (Input only)
	Method BytesLeft:Int() Property
		Return Max(Length - Position, 0)
	End

	' This specifies the number of bytes left.
	' (Based on 'RawSize' and 'DataOffset'; use at your own risk)
	Method RealBytesLeft:Int() Property
		Return Max(RawSize - DataOffset, 0)
	End

	' This may be used for asynchronous buffer-loading.
	Method DataReady:Bool() Property
		Return (Data <> Null)
	End

	' Properties (Protected):
	Protected

	Method Data:Void(Input:DataBuffer) Property
		Self._Data = Input

		Return
	End

	Method RawSize:Void(Input:Int) Property
		Self._RawSize = Input

		Return
	End

	Public

	' Fields (Public):

	' Please use any corresponding properties when possible:

	' This acts as an internal offset inside the 'Data' buffer.
	Field Offset:Int

	' The (Local) position of the stream.
	Field _Position:Int

	' The furthest point we have written in the internal buffer.
	Field _Length:Int

	' The internal buffer's size-limit. (Used when resizing)
	Field SizeLimit:Int = NOLIMIT

	' A floating-point scalar used when a resize-operation occurs.
	' Numbers will be rounded as the hardware sees fit. (Usually rounds down)
	Field ResizeScalar:Float = Default_ResizeScalar

	' Booleans / Flags:

	' This may be used to toggle resizing the internal buffer.
	Field ShouldResize:Bool

	' This specifies if big-endian byte-order should be used.
	Field BigEndianStorage:Bool

	' This specifies if this stream owns the internal buffer.
	Field OwnsBuffer:Bool

	' Fields (Protected):
	Protected

	Field _Data:DataBuffer
	Field _RawSize:Int

	Public
End

Strict

Public

' Preprocessor related:
' Nothing so far.

' Imports (Public):
Import regal.byteorder

' BRL:
Import brl.stream

' Imports (Private):
Private

Import regal.util

' BRL:
Import brl.databuffer

Public

' Classes:
Class PublicDataStream Extends Stream Implements IOnLoadDataComplete
	' Constant variable(s):
	Const NOLIMIT:Int = 0 ' -1

	' Defaults:
	Const Default_ResizeScalar:Float = 1.5 ' 2.0

	' Booleans / Flags:
	Const Default_BigEndianStorage:Bool = False

	' Constructor(s) (Public):
	Method New(Size:Int, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		GenerateBuffer(Size)

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(B:DataBuffer, Length:Int, Offset:Int=0, Copy:Bool=False, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		If (Copy) Then
			GenerateBuffer(B)

			OwnsBuffer = True
		Else
			Self.Data = B
		Endif

		Self.Offset = Offset
		Self._Length = Length

		Self.RawSize = Length

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(Path:String, BigEndianStorage:Bool=Default_BigEndianStorage, Async:Bool=False)
		If (Not Async) Then
			OnLoadDataComplete(DataBuffer.Load(Path), Path)
		Else
			DataBuffer.LoadAsync(Path, Self)
		Endif

		Self.BigEndianStorage = BigEndianStorage
	End

	' Constructor(s) (Protected):
	Protected

	Method GenerateBuffer:Void(Size:Int)
		Self.Data = New DataBuffer(Size)
		Self.RawSize = Size
		Self.OwnsBuffer = True

		Return
	End

	' This will copy the contents of 'B' into an internally managed buffer.
	Method GenerateBuffer:Void(B:DataBuffer)
		GenerateBuffer(B.Length)

		B.CopyBytes(0, Data, 0, Data.Length)

		Return
	End

	Public

	' Destructor(s):
	Method FreeBuffer:Void(DiscardBuffer:Bool=True)
		If (Data <> Null) Then
			If (DiscardBuffer) Then
				Data.Discard()
			Endif

			Data = Null
		Endif

		OwnsBuffer = False

		RawSize = 0

		Return
	End

	Method Close:Void()
		FreeBuffer(OwnsBuffer)

		Self.Offset = 0
		Self._Position = 0

		Return
	End

	' Methods:
	Method Seek:Int(Input:Int=0)
		Self._Position = Clamp(Input, 0, DataLength)

		Return Self._Position
	End

	Method Reset:Void()
		Seek()
		ResetLength()

		Return
	End

	' This states if the number of bytes specified may be read safely.
	Method WillOverReach:Bool(Bytes:Int)
		Return ReadWillOverReach(Bytes)
	End

	Method ReadWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > Length)
	End

	Method WriteWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > DataLength) ' Length
	End

	Method ReadShort:Int()
		If (BigEndianStorage) Then
			Return NToHS(Super.ReadShort())
		Endif

		Return Super.ReadShort()
	End

	Method ReadInt:Int()
		If (BigEndianStorage) Then
			Return NToHL(Super.ReadInt())
		Endif

		Return Super.ReadInt()
	End

	Method ReadFloat:Float()
		If (BigEndianStorage) Then
			Return NToHF(Super.ReadInt())
		Endif

		Return Super.ReadFloat()
	End

	Method WriteShort:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteShort(HToNS(Value))
		Else
			Super.WriteShort(Value)
		Endif

		Return
	End

	Method WriteInt:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNL(Value))
		Else
			Super.WriteInt(Value)
		Endif

		Return
	End

	Method WriteFloat:Void(Value:Float)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNF(Value))
		Else
			Super.WriteFloat(Value)
		Endif

		Return
	End

	Method Read:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		If (WillOverReach(Count)) Then
			Count = Self.BytesLeft
		Endif

		#Rem
			For Local Index:= 0 Until Count
				Buf.PokeByte(Offset+Index, Self.Data.PeekByte(Offset+Position+Index))
			Next
		#End

		Self.Data.CopyBytes(Self.DataOffset, Buf, Offset, Count)

		Self._Position += Count

		Return Count
	End

	Method Write:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		Local NewPosition:= (Count+Position)

		If (NewPosition > DataLength) Then
			If (ShouldResize) Then
				AutoResize(Count)
			Else
				Count = (DataLength-Position) ' Max(..., 0)
			Endif
		Endif

		Buf.CopyBytes(Offset, Self.Data, Self.DataOffset, Count)

		Self._Position = NewPosition

		SetLengthSafe(NewPosition)

		Return Count
	End

	' This transfers a raw segment of the internal buffer into 'S'. (Use at your own risk)
	Method TransferSegment:Void(S:Stream, Bytes:Int, Offset:Int)
		S.WriteAll(Data, Offset, Bytes)

		Return
	End

	' This may be used to transfer the internal data of this stream to another.
	Method TransferTo:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, Length, ReadOffset)

		Return
	End

	' This may be used to transfer what has already been read.
	Method TransferPastData:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, (Position-ReadOffset), ReadOffset)

		Return
	End

	' This may be used to read the number of bytes specified, from this stream then transfer it to another.
	Method TransferAmount:Void(S:Stream, Bytes:Int, Offset:Int=0)
		Local P:= S.Position

		TransferSegment(S, Bytes, (Self.Offset+Self.Position+Offset))

		S.Seek(P)

		Return
	End

	Method AutoResize:Bool(MinBytes:Int=0)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		Return Resize(Max(Int(Float(Data.Length) * ResizeScalar), MinBytes))
	End

	Method SmartResize:Bool(MinBytes:Int)
		If (MinBytes <= DataLength) Then
			Return True
		Endif

		Return Resize(MinBytes)
	End

	Method Resize:Bool(NewSize:Int, Force:Bool=False)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		If (SizeLimit <> NOLIMIT) Then
			If (NewSize > SizeLimit) Then
				If (Force) Then
					NewSize = Min(NewSize, SizeLimit)
				Else
					Return False
				Endif
			Endif
		Endif

		Data = ResizeBuffer(Data, NewSize, True, True, True)

		Return (Data <> Null)
	End

	Method SetLength:Void(Value:Int)
		Self._Length = Min(Value, DataLength)

		Return
	End

	Method SetLengthSafe:Void(Value:Int)
		SetLength(Max(Value, Self._Length))

		Return
	End

	' This sets the internal length to the current position.
	Method ClampToPosition:Void()
		SetLength(Position)

		Return
	End

	Method ResetLength:Void()
		SetLength(0)

		Return
	End

	' Call-backs:
	Method OnLoadDataComplete:Void(Data:DataBuffer, Path:String)
		If (Self.Data = Null) Then
			Self.Data = Data
			Self._Length = Data.Length
		Endif

		Return
	End

	' Properties (Public):

	' The internal I/O buffer.
	Method Data:DataBuffer() Property
		Return Self._Data
	End

	Method Eof:Int() Property
		Return (Position >= Length)
	End

	' The furthest this stream has written.
	Method Length:Int() Property
		Return Self._Length
	End

	' The number of bytes into the buffer this stream is.
	Method Position:Int() Property
		Return Self._Position
	End

	' The overall length of the 'Data' buffer. (Taking 'Offset' into account)
	Method RawSize:Int() Property
		Return Self._RawSize
	End

	' This will return the 'RawSize' property adjusted to the internal offset.
	Method DataLength:Int() Property
		Return (RawSize - Offset)
	End

	' The real position in the 'Data' buffer. (After 'Offset')
	Method DataOffset:Int() Property
		Return (Offset+Position)
	End

	' The number of bytes left in 'Data'. (Input only)
	Method BytesLeft:Int() Property
		Return Max(Length - Position, 0)
	End

	' This specifies the number of bytes left.
	' (Based on 'RawSize' and 'DataOffset'; use at your own risk)
	Method RealBytesLeft:Int() Property
		Return Max(RawSize - DataOffset, 0)
	End

	' This may be used for asynchronous buffer-loading.
	Method DataReady:Bool() Property
		Return (Data <> Null)
	End

	' Properties (Protected):
	Protected

	Method Data:Void(Input:DataBuffer) Property
		Self._Data = Input

		Return
	End

	Method RawSize:Void(Input:Int) Property
		Self._RawSize = Input

		Return
	End

	Public

	' Fields (Public):

	' Please use any corresponding properties when possible:

	' This acts as an internal offset inside the 'Data' buffer.
	Field Offset:Int

	' The (Local) position of the stream.
	Field _Position:Int

	' The furthest point we have written in the internal buffer.
	Field _Length:Int

	' The internal buffer's size-limit. (Used when resizing)
	Field SizeLimit:Int = NOLIMIT

	' A floating-point scalar used when a resize-operation occurs.
	' Numbers will be rounded as the hardware sees fit. (Usually rounds down)
	Field ResizeScalar:Float = Default_ResizeScalar

	' Booleans / Flags:

	' This may be used to toggle resizing the internal buffer.
	Field ShouldResize:Bool

	' This specifies if big-endian byte-order should be used.
	Field BigEndianStorage:Bool

	' This specifies if this stream owns the internal buffer.
	Field OwnsBuffer:Bool

	' Fields (Protected):
	Protected

	Field _Data:DataBuffer
	Field _RawSize:Int

	Public
End


CameraFX test updated

CameraFX test updated

Strict

Import flixel
Import flixel.flxtext.driver.angelfont

#REFLECTION_FILTER="camerafx*|flixel*"

Function Main:Int()
	New CameraFX()
	Return 0
End Function

Class CameraFX Extends FlxGame

	Method New()
		Super.New(640, 480, GetClass("CameraFXState"))
	End Method

	Method OnContentInit:Void()
		Local minSize:Int = FlxAssetsManager.GetFont(FlxText.SYSTEM_FONT).MinSize
		Local maxSize:Int = FlxAssetsManager.GetFont(FlxText.SYSTEM_FONT).MaxSize

		FlxTextAngelFontDriver.Init()
		FlxText.SetDefaultDriver(ClassInfo(FlxTextAngelFontDriver.ClassObject))
	End Method

End Class

Class CameraFXState Extends FlxState

	Method Create:Void()
		Local manual:FlxText = New FlxText(10, 10, 620, "- Press S to shake")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)

		manual = New FlxText(10, 40, 620, "- Press L to flash")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)

		manual = New FlxText(10, 70, 620, "- Press F to fade")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)

		manual = New FlxText(10, 100, 620, "- Press SPACE to stopFX")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)
	End Method

	Method Update:Void()
		If (FlxG.Keys.JustPressed(KEY_S)) Then
			FlxG.Shake()
		End If

		If (FlxG.Keys.JustPressed(KEY_L)) Then
			FlxG.Flash()
		End If

		If (FlxG.Keys.JustPressed(KEY_F)) Then
			FlxG.Fade()
		End If

		If (FlxG.Keys.JustPressed(KEY_SPACE)) Then
			FlxG.Camera.StopFX()
		End If

		Super.Update()
	End Method

End Class

Strict

Import flixel
Import flixel.flxtext.driver.angelfont

Function Main:Int()
	New CameraFX()
	Return 0
End Function

Class CameraFX Extends FlxGame

	Method New()
		Super.New(640, 480, CameraFXState.ClassObject)
	End Method

	Method OnContentInit:Void()
		Local minSize:Int = FlxAssetsManager.GetFont(FlxText.SYSTEM_FONT).MinSize
		Local maxSize:Int = FlxAssetsManager.GetFont(FlxText.SYSTEM_FONT).MaxSize

		FlxTextAngelFontDriver.Init()
		FlxText.SetDefaultDriver(AngelfontTextDriver)
	End Method

End Class

Class CameraFXStateClass Implements FlxClass

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

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

End Class

Class CameraFXState Extends FlxState

	Global ClassObject:FlxClass = new CameraFXStateClass()

	Method Create:Void()
		Local manual:FlxText = New FlxText(10, 10, 620, "- Press S to shake")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)

		manual = New FlxText(10, 40, 620, "- Press L to flash")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)

		manual = New FlxText(10, 70, 620, "- Press F to fade")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)

		manual = New FlxText(10, 100, 620, "- Press SPACE to stopFX")
		manual.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_LEFT)
		Add(manual)
	End Method

	Method Update:Void()
		If (FlxG.Keys.JustPressed(KEY_S)) Then
			FlxG.Shake()
		End If

		If (FlxG.Keys.JustPressed(KEY_L)) Then
			FlxG.Flash()
		End If

		If (FlxG.Keys.JustPressed(KEY_F)) Then
			FlxG.Fade()
		End If

		If (FlxG.Keys.JustPressed(KEY_SPACE)) Then
			FlxG.Camera.StopFX()
		End If

		Super.Update()
	End Method

End Class


reset TexturePacker.defaultImageLoader in test TearDown

Strict

Private

Import bono
Import mojo

Public

Class TexturePackerTest Extends AppTestCase
    Field mockLoader:MockImageLoader

    Method SetUp:Void()
        mockLoader = New MockImageLoader()
        TexturePacker.defaultImageLoader = mockLoader
    End

    Method TearDown:Void()
        TexturePacker.defaultImageLoader = Null
    End

    Method TestWithExampleSprite:Void()
        Local tp := New TexturePacker(
            "io/texturepacker/sprite/sprite.xml",
            New MojoImageLoader())

        RenderAndCompare(tp, "rect-green.png")

        MarkTestSkipped("This should work but AssertScreenEquals seems to be buggy")
        ' RenderAndCompare(tp, "rect-blue.png")
        ' RenderAndCompare(tp, "rect-red.png")
    End

    Method TestGetFilename:Void()
        Local file := GetXmlFile("valid-1x1")
        Local tp := New TexturePacker(file)

        AssertEquals(file, tp.GetFilename())
    End

    Method TestGetRootImage:Void()
        Local file := GetXmlFile("valid-1x1")
        Local tp := New TexturePacker(file)

        AssertNotNull(tp.GetRootImage())
        AssertEquals(1, tp.GetRootImage().Width())
        AssertEquals(1, tp.GetRootImage().Height())
    End

    Method RenderAndCompare:Void(tp:TexturePacker, file:String)
        Cls()
        DrawImage(tp.Get(file), 0, 0)
        AssertScreenEquals("io/texturepacker/sprite/" + file)
    End

    Method TestFailOnWrongConstructor:Void()
        Local caught := False
        Try
            New TexturePacker()
        Catch ex:InvalidConstructorException
            caught = True
        End
        If Not caught Then Fail("InvalidConstructorException expected")
    End

    Method TestExceptionOnInvalidFile:Void()
        Local caught := False
        Try
            New TexturePacker("some-invalid-filename")
        Catch ex:RuntimeException
            caught = True
        End
        If Not caught Then Fail("RuntimeException expected")
    End

    Method TestExceptionOnWrongRootImageWidht:Void()
        mockLoader.size.x = 2
        LoadAndExpectAssertionExeption(GetXmlFile("valid-1x1"))
    End

    Method TestExceptionOnWrongRootImageHeight:Void()
        mockLoader.size.y = 2
        LoadAndExpectAssertionExeption(GetXmlFile("valid-1x1"))
    End

    Method TestRootImageWidthAndHeightAreOptional:Void()
        mockLoader.size.Set(2, 2)
        New TexturePacker(GetXmlFile("valid-no-root-image-dimension"))
    End

    Method TestFailOnInvalidXml:Void()
        Local files := ["unknown-child-node", "missing-attr-n",
            "missing-attr-x", "missing-attr-y",
            "missing-attr-w", "missing-attr-h",
            "todo-attr-ox", "todo-attr-oy",
            "todo-attr-ow", "todo-attr-oh",
            "todo-attr-r", "wrong-value-r"]

        For Local file := EachIn files
            LoadAndExpectXmlExeption(GetXmlFile(file))
        End
    End

    Method GetXmlFile:String(name:String)
        Return "io/texturepacker/" + name + ".xml"
    End

    Method LoadAndExpectAssertionExeption:Void(file:String)
        Local caught := False
        Try
            New TexturePacker(file)
        Catch ex:AssertionFailedException
            caught = True
        End

        If Not caught
            Fail("AssertionFailedException expected for file " + file)
        End
    End

    Method LoadAndExpectXmlExeption:Void(file:String)
        Local caught := False
        Try
            New TexturePacker(file)
        Catch ex:InvalidXmlException
            caught = True
        End

        If Not caught
            Fail("InvalidXmlException expected for file " + file)
        End
    End
End

Strict

Private

Import bono
Import mojo

Public

Class TexturePackerTest Extends AppTestCase
    Field mockLoader:MockImageLoader

    Method SetUp:Void()
        mockLoader = New MockImageLoader()
        TexturePacker.defaultImageLoader = mockLoader
    End

    Method TestWithExampleSprite:Void()
        Local tp := New TexturePacker(
            "io/texturepacker/sprite/sprite.xml",
            New MojoImageLoader())

        RenderAndCompare(tp, "rect-green.png")

        MarkTestSkipped("This should work but AssertScreenEquals seems to be buggy")
        ' RenderAndCompare(tp, "rect-blue.png")
        ' RenderAndCompare(tp, "rect-red.png")
    End

    Method TestGetFilename:Void()
        Local file := GetXmlFile("valid-1x1")
        Local tp := New TexturePacker(file)

        AssertEquals(file, tp.GetFilename())
    End

    Method TestGetRootImage:Void()
        Local file := GetXmlFile("valid-1x1")
        Local tp := New TexturePacker(file)

        AssertNotNull(tp.GetRootImage())
        AssertEquals(1, tp.GetRootImage().Width())
        AssertEquals(1, tp.GetRootImage().Height())
    End

    Method RenderAndCompare:Void(tp:TexturePacker, file:String)
        Cls()
        DrawImage(tp.Get(file), 0, 0)
        AssertScreenEquals("io/texturepacker/sprite/" + file)
    End

    Method TestFailOnWrongConstructor:Void()
        Local caught := False
        Try
            New TexturePacker()
        Catch ex:InvalidConstructorException
            caught = True
        End
        If Not caught Then Fail("InvalidConstructorException expected")
    End

    Method TestExceptionOnInvalidFile:Void()
        Local caught := False
        Try
            New TexturePacker("some-invalid-filename")
        Catch ex:RuntimeException
            caught = True
        End
        If Not caught Then Fail("RuntimeException expected")
    End

    Method TestExceptionOnWrongRootImageWidht:Void()
        mockLoader.size.x = 2
        LoadAndExpectAssertionExeption(GetXmlFile("valid-1x1"))
    End

    Method TestExceptionOnWrongRootImageHeight:Void()
        mockLoader.size.y = 2
        LoadAndExpectAssertionExeption(GetXmlFile("valid-1x1"))
    End

    Method TestRootImageWidthAndHeightAreOptional:Void()
        mockLoader.size.Set(2, 2)
        New TexturePacker(GetXmlFile("valid-no-root-image-dimension"))
    End

    Method TestFailOnInvalidXml:Void()
        Local files := ["unknown-child-node", "missing-attr-n",
            "missing-attr-x", "missing-attr-y",
            "missing-attr-w", "missing-attr-h",
            "todo-attr-ox", "todo-attr-oy",
            "todo-attr-ow", "todo-attr-oh",
            "todo-attr-r", "wrong-value-r"]

        For Local file := EachIn files
            LoadAndExpectXmlExeption(GetXmlFile(file))
        End
    End

    Method GetXmlFile:String(name:String)
        Return "io/texturepacker/" + name + ".xml"
    End

    Method LoadAndExpectAssertionExeption:Void(file:String)
        Local caught := False
        Try
            New TexturePacker(file)
        Catch ex:AssertionFailedException
            caught = True
        End

        If Not caught
            Fail("AssertionFailedException expected for file " + file)
        End
    End

    Method LoadAndExpectXmlExeption:Void(file:String)
        Local caught := False
        Try
            New TexturePacker(file)
        Catch ex:InvalidXmlException
            caught = True
        End

        If Not caught
            Fail("InvalidXmlException expected for file " + file)
        End
    End
End


FlxTilemap destroy fixed

FlxTilemap destroy fixed

Strict

Import mojo

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

Import system.flxtile
Import system.flxtilemapbuffer

Import "data/flx_autotiles.png"
Import "data/flx_autotiles_alt.png"

Class FlxTilemap Extends FlxObject

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

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

	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:Stack<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 Stack<FlxTilemapBuffer>()
		_tileWidth = 0
		_tileHeight = 0
		_tiles = Null
		immovable = True
		_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
		Wend

		_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

		Return Self
	End Method

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

		_camera = FlxG._currentCamera

		If (FlxG._currentCameraID >= _buffers.Length()) _buffers.Push(Null)
		_buffer = _buffers.Get(FlxG._currentCameraID)

		If (_buffer = Null) Then
			_buffer = New FlxTilemapBuffer(_tileWidth, _tileHeight, widthInTiles, heightInTiles, _camera)
			_buffers.Set(FlxG._currentCameraID, _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
			Local tile:FlxTile

			_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
				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, 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)
		Local i:Int = 0
		Local l:Int = _buffers.Length()

		While (i < l)
			_buffers.Get(i).dirty = dirty
			i += 1
		Wend
	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(x, y, 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 tile.filter.InstanceOf(object))) 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 tile.filter.InstanceOf(object))) 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:FlxClass, 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 = FlxCamera
		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

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

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 flxextern
Import flxobject
Import flxrect
Import flxg
Import flxcamera
Import flxgroup
Import flxbasic
Import flxpath

Import system.flxtile
Import system.flxtilemapbuffer

Import "data/flx_autotiles.png"
Import "data/flx_autotiles_alt.png"

Class FlxTilemap Extends FlxObject

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

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

	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:Stack<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 Stack<FlxTilemapBuffer>()
		_tileWidth = 0
		_tileHeight = 0
		_tiles = Null
		immovable = True
		_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)
			_tileObjects[i].Destroy()
			_tileObjects[i] = Null
		Wend

		_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

		Return Self
	End Method

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

		_camera = FlxG._currentCamera

		If (FlxG._currentCameraID >= _buffers.Length()) _buffers.Push(Null)
		_buffer = _buffers.Get(FlxG._currentCameraID)

		If (_buffer = Null) Then
			_buffer = New FlxTilemapBuffer(_tileWidth, _tileHeight, widthInTiles, heightInTiles, _camera)
			_buffers.Set(FlxG._currentCameraID, _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
			Local tile:FlxTile

			_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
				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, 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)
		Local i:Int = 0
		Local l:Int = _buffers.Length()

		While (i < l)
			_buffers.Get(i).dirty = dirty
			i += 1
		Wend
	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(x, y, 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 tile.filter.InstanceOf(object))) 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 tile.filter.InstanceOf(object))) 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:FlxClass, 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 = FlxCamera
		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

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

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