Untitled diff
701 Zeilen
#Rem
#Rem
Copyright (c) 2011 Steve Revill and Shane Woolcock
Copyright (c) 2011 Steve Revill and Shane Woolcock
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#End
#End
Strict
Strict
Import xml
Import xml
Import base64
Import base64
Import mojo
Import mojo
Import brl.filepath
Import brl.filepath
' TileMapPropertyContainer
' TileMapPropertyContainer
'Summary: Classes that extend this will automatically instantiate a property container and expose it.
'Summary: Classes that extend this will automatically instantiate a property container and expose it.
Class TileMapPropertyContainer
Class TileMapPropertyContainer
Private
Private
	Field properties:TileMapProperties = New TileMapProperties
	Field properties:TileMapProperties = New TileMapProperties
Public
Public
	Method Properties:TileMapProperties() Property
	Method Properties:TileMapProperties() Property
		Return properties
		Return properties
	End
	End
End
End
' ITileMapPostLoad
' ITileMapPostLoad
'Summary: Classes that implement this interface will have PostLoad automatically called once the object has been created.
'Summary: Classes that implement this interface will have PostLoad automatically called once the object has been created.
Interface ITileMapPostLoad
Interface ITileMapPostLoad
	Method PostLoad:Void()
	Method PostLoad:Void()
End
End
' TileMapReader
' TileMapReader
'Summary: This can be extended to handle any map file format.
'Summary: This can be extended to handle any map file format.
Class TileMapReader Abstract
Class TileMapReader Abstract
	Field tileMap:TileMap
	Field tileMap:TileMap
	Field graphicsPath:String
	Field graphicsPath:String
	Method LoadMap:TileMap(filename:String) Abstract
	Method LoadMap:TileMap(filename:String) Abstract
	' override this to create a custom tilemap class
	' override this to create a custom tilemap class
	Method CreateMap:TileMap()
	Method CreateMap:TileMap()
		Return New TileMap
		Return New TileMap
	End
	End
End
End
' TiledTileMapReader
' TiledTileMapReader
'Summary: Extends TileMapReader to add support for the Tiled map editor.
'Summary: Extends TileMapReader to add support for the Tiled map editor.
Class TiledTileMapReader Extends TileMapReader
Class TiledTileMapReader Extends TileMapReader
	Field doc:XMLDoc
	Field doc:XMLDoc
	' Overrides TileMapReader
	' Overrides TileMapReader
	Method LoadMap:TileMap(filename:String)
	Method LoadMap:TileMap(filename:String)
		' open file and get root node
		' open file and get root node
		Local xmlString:String = LoadString(filename)
		Local xmlString:String = LoadString(filename)
		' error if we couldnt load the file
		' error if we couldnt load the file
		If Not xmlString
		If Not xmlString
			Error("Cannot load tile map file " + filename + ". Ensure you have TMX in the in the allowed #TEXT_FILES")
			Error("Cannot load tile map file " + filename + ". Ensure you have TMX in the in the allowed #TEXT_FILES")
		End
		End
		' look for the data encoding, if we cant find it assume its RAW XML and thats just too slow!
		' look for the data encoding, if we cant find it assume its RAW XML and thats just too slow!
		Local findData:Int = xmlString.Find("<data encoding")
		Local findData:Int = xmlString.Find("<data encoding")
		If findData = -1
		If findData = -1
			Print("Tiled Raw XML is not supported!")
			Print("Tiled Raw XML is not supported!")
		End
		End
		'Set the root graphics path relative to this file's location.
		graphicsPath = ExtractDir(filename) + "/"
		doc = ParseXML(xmlString)
		doc = ParseXML(xmlString)
		Return ReadMap(doc)
		Return ReadMap(doc)
	End
	End
	Method ReadMap:TileMap(node:XMLNode)
	Method ReadMap:TileMap(node:XMLNode)
		tileMap = CreateMap()
		tileMap = CreateMap()
		ReadProperties(node, tileMap)
		ReadProperties(node, tileMap)
		' extract map properties
		' extract map properties
		If tileMap.properties.Has(PROP_MAP_WRAP_X) Then tileMap.wrapX = tileMap.properties.Get(PROP_MAP_WRAP_X).GetBool()
		If tileMap.properties.Has(PROP_MAP_WRAP_X) Then tileMap.wrapX = tileMap.properties.Get(PROP_MAP_WRAP_X).GetBool()
		If tileMap.properties.Has(PROP_MAP_WRAP_Y) Then tileMap.wrapY = tileMap.properties.Get(PROP_MAP_WRAP_Y).GetBool()
		If tileMap.properties.Has(PROP_MAP_WRAP_Y) Then tileMap.wrapY = tileMap.properties.Get(PROP_MAP_WRAP_Y).GetBool()
		' read root node's attributes
		' read root node's attributes
		If node.HasAttribute(ATTR_MAP_VERSION) Then tileMap.version = node.GetAttribute(ATTR_MAP_VERSION)
		If node.HasAttribute(ATTR_MAP_VERSION) Then tileMap.version = node.GetAttribute(ATTR_MAP_VERSION)
		If node.HasAttribute(ATTR_MAP_ORIENTATION) Then tileMap.orientation = node.GetAttribute(ATTR_MAP_ORIENTATION)
		If node.HasAttribute(ATTR_MAP_ORIENTATION) Then tileMap.orientation = node.GetAttribute(ATTR_MAP_ORIENTATION)
		If node.HasAttribute(ATTR_MAP_WIDTH) Then tileMap.width = Int(node.GetAttribute(ATTR_MAP_WIDTH))
		If node.HasAttribute(ATTR_MAP_WIDTH) Then tileMap.width = Int(node.GetAttribute(ATTR_MAP_WIDTH))
		If node.HasAttribute(ATTR_MAP_HEIGHT) Then tileMap.height = Int(node.GetAttribute(ATTR_MAP_HEIGHT))
		If node.HasAttribute(ATTR_MAP_HEIGHT) Then tileMap.height = Int(node.GetAttribute(ATTR_MAP_HEIGHT))
		If node.HasAttribute(ATTR_MAP_TILEWIDTH) Then tileMap.tileWidth = Int(node.GetAttribute(ATTR_MAP_TILEWIDTH))
		If node.HasAttribute(ATTR_MAP_TILEWIDTH) Then tileMap.tileWidth = Int(node.GetAttribute(ATTR_MAP_TILEWIDTH))
		If node.HasAttribute(ATTR_MAP_TILEHEIGHT) Then tileMap.tileHeight = Int(node.GetAttribute(ATTR_MAP_TILEHEIGHT))
		If node.HasAttribute(ATTR_MAP_TILEHEIGHT) Then tileMap.tileHeight = Int(node.GetAttribute(ATTR_MAP_TILEHEIGHT))
		tileMap.maxTileWidth = tileMap.tileWidth
		tileMap.maxTileWidth = tileMap.tileWidth
		tileMap.maxTileHeight = tileMap.tileHeight
		tileMap.maxTileHeight = tileMap.tileHeight
		' parse children
		' parse children
		If Not node.children.IsEmpty() Then
		If Not node.children.IsEmpty() Then
			For Local mapchild:XMLNode = Eachin node.children
			For Local mapchild:XMLNode = Eachin node.children
				' tileset
				' tileset
				If mapchild.name = NODE_TILESET Then
				If mapchild.name = NODE_TILESET Then
					Local ts:TileMapTileset = ReadTileset(mapchild)
					Local ts:TileMapTileset = ReadTileset(mapchild)
					tileMap.tilesets.Set(ts.name, ts)
					tileMap.tilesets.Set(ts.name, ts)
				' tile layer
				' tile layer
				Elseif mapchild.name = NODE_LAYER Then
				Elseif mapchild.name = NODE_LAYER Then
					Local layer:TileMapLayer = ReadTileLayer(mapchild)
					Local layer:TileMapLayer = ReadTileLayer(mapchild)
					tileMap.layers.Push(layer)
					tileMap.layers.Push(layer)
				' object layer
				' object layer
				Elseif mapchild.name = NODE_OBJECTGROUP Then
				Elseif mapchild.name = NODE_OBJECTGROUP Then
					Local layer:TileMapLayer = ReadObjectLayer(mapchild)
					Local layer:TileMapLayer = ReadObjectLayer(mapchild)
					tileMap.layers.Push(layer)
					tileMap.layers.Push(layer)
				Endif
				Endif
			Next
			Next
		Endif
		Endif
		DoPostLoad(tileMap)
		DoPostLoad(tileMap)
		Return tileMap
		Return tileMap
	End
	End
	Method DoPostLoad:Void(obj:Object)
	Method DoPostLoad:Void(obj:Object)
		If ITileMapPostLoad(obj) <> Null Then ITileMapPostLoad(obj).PostLoad()
		If ITileMapPostLoad(obj) <> Null Then ITileMapPostLoad(obj).PostLoad()
	End
	End
	Method ReadProperties:Void(node:XMLNode, obj:Object)
	Method ReadProperties:Void(node:XMLNode, obj:Object)
		Local cont:TileMapPropertyContainer = TileMapPropertyContainer(obj)
		Local cont:TileMapPropertyContainer = TileMapPropertyContainer(obj)
		If cont <> Null Then
		If cont <> Null Then
			For Local propNode:XMLNode = EachIn node.children
			For Local propNode:XMLNode = EachIn node.children
				If propNode.name = NODE_PROPERTIES Then
				If propNode.name = NODE_PROPERTIES Then
					For Local child:XMLNode = EachIn propNode.children
					For Local child:XMLNode = EachIn propNode.children
						If child.name = NODE_PROPERTY Then
						If child.name = NODE_PROPERTY Then
							Local prop:TileMapProperty = ReadProperty(child)
							Local prop:TileMapProperty = ReadProperty(child)
							cont.properties.props.Set(prop.name, prop)
							cont.properties.props.Set(prop.name, prop)
						Endif
						Endif
					Next
					Next
					Return
					Return
				End
				End
			Next
			Next
		End
		End
	End
	End
	Method ReadProperty:TileMapProperty(node:XMLNode)
	Method ReadProperty:TileMapProperty(node:XMLNode)
		Return New TileMapProperty(node.GetAttribute(ATTR_PROPERTY_NAME, "default"), node.GetAttribute(ATTR_PROPERTY_VALUE, ""))
		Return New TileMapProperty(node.GetAttribute(ATTR_PROPERTY_NAME, "default"), node.GetAttribute(ATTR_PROPERTY_VALUE, ""))
	End
	End
	Method ReadTileset:TileMapTileset(node:XMLNode, target:TileMapTileset=Null)
	Method ReadTileset:TileMapTileset(node:XMLNode, target:TileMapTileset=Null)
		Local rv:TileMapTileset = target
		Local rv:TileMapTileset = target
		ReadProperties(node, rv)
		ReadProperties(node, rv)
		If rv = Null Then rv = tileMap.CreateTileset()
		If rv = Null Then rv = tileMap.CreateTileset()
		If node.HasAttribute(ATTR_TILESET_FIRSTGID) Then rv.firstGid = Int(node.GetAttribute(ATTR_TILESET_FIRSTGID))
		If node.HasAttribute(ATTR_TILESET_FIRSTGID) Then rv.firstGid = Int(node.GetAttribute(ATTR_TILESET_FIRSTGID))
		If node.HasAttribute(ATTR_TILESET_SOURCE) Then
		If node.HasAttribute(ATTR_TILESET_SOURCE) Then
			rv.source = node.GetAttribute(ATTR_TILESET_SOURCE)
			rv.source = node.GetAttribute(ATTR_TILESET_SOURCE)
			Local tilesetdoc:XMLDoc = ParseXML(LoadString(rv.source))   'Note:  Fragile... Let's make this more robust later -nobu
			Local tilesetdoc:XMLDoc = ParseXML(LoadString(rv.source))   'Note:  Fragile... Let's make this more robust later -nobu
			Return ReadTileset(tilesetdoc, rv)
			Return ReadTileset(tilesetdoc, rv)
		Else
		Else
			If node.HasAttribute(ATTR_TILESET_NAME) Then rv.name = node.GetAttribute(ATTR_TILESET_NAME)
			If node.HasAttribute(ATTR_TILESET_NAME) Then rv.name = node.GetAttribute(ATTR_TILESET_NAME)
			If node.HasAttribute(ATTR_TILESET_TILEWIDTH) Then rv.tileWidth = Int(node.GetAttribute(ATTR_TILESET_TILEWIDTH))
			If node.HasAttribute(ATTR_TILESET_TILEWIDTH) Then rv.tileWidth = Int(node.GetAttribute(ATTR_TILESET_TILEWIDTH))
			If node.HasAttribute(ATTR_TILESET_TILEHEIGHT) Then rv.tileHeight = Int(node.GetAttribute(ATTR_TILESET_TILEHEIGHT))
			If node.HasAttribute(ATTR_TILESET_TILEHEIGHT) Then rv.tileHeight = Int(node.GetAttribute(ATTR_TILESET_TILEHEIGHT))
			If node.HasAttribute(ATTR_TILESET_SPACING) Then rv.spacing = Int(node.GetAttribute(ATTR_TILESET_SPACING))
			If node.HasAttribute(ATTR_TILESET_SPACING) Then rv.spacing = Int(node.GetAttribute(ATTR_TILESET_SPACING))
			If node.HasAttribute(ATTR_TILESET_MARGIN) Then rv.margin = Int(node.GetAttribute(ATTR_TILESET_MARGIN))
			If node.HasAttribute(ATTR_TILESET_MARGIN) Then rv.margin = Int(node.GetAttribute(ATTR_TILESET_MARGIN))
			If Not node.children.IsEmpty() Then
			If Not node.children.IsEmpty() Then
				For Local child:XMLNode = Eachin node.children
				For Local child:XMLNode = Eachin node.children
					If child.name = NODE_IMAGE Then
					If child.name = NODE_IMAGE Then
						rv.imageNode = ReadImage(child)
						rv.imageNode = ReadImage(child)
					Elseif child.name = NODE_TILE Then
					Elseif child.name = NODE_TILE Then
						rv.tileNodes.Push(ReadTile(child))
						rv.tileNodes.Push(ReadTile(child))
					End
					End
				Next
				Next
			End
			End
		End
		End
		DoPostLoad(rv)
		DoPostLoad(rv)
		Return rv
		Return rv
	End
	End
	Method ReadLayerAttributes:Void(node:XMLNode, layer:TileMapLayer)
	Method ReadLayerAttributes:Void(node:XMLNode, layer:TileMapLayer)
		If node.HasAttribute(ATTR_LAYER_NAME) Then layer.name = node.GetAttribute(ATTR_LAYER_NAME)
		If node.HasAttribute(ATTR_LAYER_NAME) Then layer.name = node.GetAttribute(ATTR_LAYER_NAME)
		If node.HasAttribute(ATTR_LAYER_WIDTH) Then layer.width = Int(node.GetAttribute(ATTR_LAYER_WIDTH))
		If node.HasAttribute(ATTR_LAYER_WIDTH) Then layer.width = Int(node.GetAttribute(ATTR_LAYER_WIDTH))
		If node.HasAttribute(ATTR_LAYER_HEIGHT) Then layer.height = Int(node.GetAttribute(ATTR_LAYER_HEIGHT))
		If node.HasAttribute(ATTR_LAYER_HEIGHT) Then layer.height = Int(node.GetAttribute(ATTR_LAYER_HEIGHT))
		layer.visible = Not node.HasAttribute(ATTR_LAYER_VISIBLE) Or Int(node.GetAttribute(ATTR_LAYER_VISIBLE)) <> 0
		layer.visible = Not node.HasAttribute(ATTR_LAYER_VISIBLE) Or Int(node.GetAttribute(ATTR_LAYER_VISIBLE)) <> 0
		If node.HasAttribute(ATTR_LAYER_OPACITY) Then layer.opacity = Float(node.GetAttribute(ATTR_LAYER_OPACITY))
		If node.HasAttribute(ATTR_LAYER_OPACITY) Then layer.opacity = Float(node.GetAttribute(ATTR_LAYER_OPACITY))
	End
	End
	Method ReadTileLayer:TileMapTileLayer(node:XMLNode)
	Method ReadTileLayer:TileMapTileLayer(node:XMLNode)
		Local rv:TileMapTileLayer = tileMap.CreateTileLayer()
		Local rv:TileMapTileLayer = tileMap.CreateTileLayer()
		ReadProperties(node, rv)
		ReadProperties(node, rv)
		ReadLayerAttributes(node, rv)
		ReadLayerAttributes(node, rv)
		If rv.properties.Has(PROP_LAYER_PARALLAX_OFFSET_X) Then rv.parallaxOffsetX = rv.properties.Get(PROP_LAYER_PARALLAX_OFFSET_X).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_OFFSET_X) Then rv.parallaxOffsetX = rv.properties.Get(PROP_LAYER_PARALLAX_OFFSET_X).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_OFFSET_Y) Then rv.parallaxOffsetY = rv.properties.Get(PROP_LAYER_PARALLAX_OFFSET_Y).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_OFFSET_Y) Then rv.parallaxOffsetY = rv.properties.Get(PROP_LAYER_PARALLAX_OFFSET_Y).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_SCALE_X) Then rv.parallaxScaleX = rv.properties.Get(PROP_LAYER_PARALLAX_SCALE_X).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_SCALE_X) Then rv.parallaxScaleX = rv.properties.Get(PROP_LAYER_PARALLAX_SCALE_X).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_SCALE_Y) Then rv.parallaxScaleY = rv.properties.Get(PROP_LAYER_PARALLAX_SCALE_Y).GetFloat()
		If rv.properties.Has(PROP_LAYER_PARALLAX_SCALE_Y) Then rv.parallaxScaleY = rv.properties.Get(PROP_LAYER_PARALLAX_SCALE_Y).GetFloat()
		If rv.properties.Has(PROP_MAP_WRAP_X) Then rv.wrapX = rv.properties.Get(PROP_MAP_WRAP_X).GetBool()
		If rv.properties.Has(PROP_MAP_WRAP_Y) Then rv.wrapY = rv.properties.Get(PROP_MAP_WRAP_Y).GetBool()
		For Local child:XMLNode = Eachin node.children
		For Local child:XMLNode = Eachin node.children
			If child.name = NODE_DATA Then
			If child.name = NODE_DATA Then
				rv.mapData = ReadTileData(child, rv)
				rv.mapData = ReadTileData(child, rv)
			End
			End
		Next
		Next
		DoPostLoad(rv)
		DoPostLoad(rv)
		Return rv
		Return rv
	End
	End
	Method ReadObjectLayer:TileMapObjectLayer(node:XMLNode)
	Method ReadObjectLayer:TileMapObjectLayer(node:XMLNode)
		Local rv:TileMapObjectLayer = tileMap.CreateObjectLayer()
		Local rv:TileMapObjectLayer = tileMap.CreateObjectLayer()
		ReadProperties(node, rv)
		ReadProperties(node, rv)
		ReadLayerAttributes(node, rv)
		ReadLayerAttributes(node, rv)
		If node.HasAttribute(ATTR_OBJECTGROUP_COLOR) Then rv.color = ColorToInt(node.GetAttribute(ATTR_OBJECTGROUP_COLOR))
		If node.HasAttribute(ATTR_OBJECTGROUP_COLOR) Then rv.color = ColorToInt(node.GetAttribute(ATTR_OBJECTGROUP_COLOR))
		For Local child:XMLNode = Eachin node.children
		For Local child:XMLNode = Eachin node.children
			If child.name = NODE_OBJECT Then
			If child.name = NODE_OBJECT Then
				rv.objects.Push(ReadObject(child, rv))
				rv.objects.Push(ReadObject(child, rv))
			End
			End
		Next
		Next
		DoPostLoad(rv)
		DoPostLoad(rv)
		Return rv
		Return rv
	End
	End
	Method ReadImage:TileMapImage(node:XMLNode)
	Method ReadImage:TileMapImage(node:XMLNode)
		Local rv:TileMapImage = tileMap.CreateImage()
		Local rv:TileMapImage = tileMap.CreateImage()
		ReadProperties(node, rv)
		ReadProperties(node, rv)
		If node.HasAttribute(ATTR_IMAGE_SOURCE) Then rv.source = graphicsPath + StripDir(node.GetAttribute(ATTR_IMAGE_SOURCE))
		If node.HasAttribute(ATTR_IMAGE_SOURCE) Then  'Try to find a valid graphics resource.
			Local src:= node.GetAttribute(ATTR_IMAGE_SOURCE)
			If src[0 .. 7].Contains(":/") Or src[0 .. 7].Contains(":\") 'Hardcoded path or URL.
				rv.source = src
			Else  'Relative path. Use graphicsPath from loaded mapfile.
				rv.source = graphicsPath + src		
			End If
		End If
		If node.HasAttribute(ATTR_IMAGE_WIDTH) Then rv.width = Int(node.GetAttribute(ATTR_IMAGE_WIDTH))
		If node.HasAttribute(ATTR_IMAGE_WIDTH) Then rv.width = Int(node.GetAttribute(ATTR_IMAGE_WIDTH))
		If node.HasAttribute(ATTR_IMAGE_HEIGHT) Then rv.height = Int(node.GetAttribute(ATTR_IMAGE_HEIGHT))
		If node.HasAttribute(ATTR_IMAGE_HEIGHT) Then rv.height = Int(node.GetAttribute(ATTR_IMAGE_HEIGHT))
		If node.HasAttribute(ATTR_IMAGE_TRANS) Then rv.trans = node.GetAttribute(ATTR_IMAGE_TRANS)
		If node.HasAttribute(ATTR_IMAGE_TRANS) Then rv.trans = node.GetAttribute(ATTR_IMAGE_TRANS)
		If rv.trans.Length > 0 Then
		If rv.trans.Length > 0 Then
			rv.transR = HexToDec(rv.trans[0..2])
			rv.transR = HexToDec(rv.trans[0..2])
			rv.transG = HexToDec(rv.trans[2..4])
			rv.transG = HexToDec(rv.trans[2..4])
			rv.transB = HexToDec(rv.trans[4..6])
			rv.transB = HexToDec(rv.trans[4..6])
		Endif
		Endif
		DoPostLoad(rv)
		DoPostLoad(rv)
		Return rv
		Return rv
	End
	End
	Method ReadTile:TileMapTile(node:XMLNode)
	Method ReadTile:TileMapTile(node:XMLNode)
		Local id:Int = Int(node.GetAttribute(ATTR_TILE_ID, "0"))
		Local id:Int = Int(node.GetAttribute(ATTR_TILE_ID, "0"))
		Local rv:TileMapTile = tileMap.CreateTile(id)
		Local rv:TileMapTile = tileMap.CreateTile(id)
		ReadProperties(node, rv)
		ReadProperties(node, rv)
		DoPostLoad(rv)
		DoPostLoad(rv)
		Return rv
		Return rv
	End
	End
	Method ReadObject:TileMapObject(node:XMLNode, layer:TileMapObjectLayer)
	Method ReadObject:TileMapObject(node:XMLNode, layer:TileMapObjectLayer)
		Local rv:TileMapObject = tileMap.CreateObject()
		Local rv:TileMapObject = tileMap.CreateObject()
		ReadProperties(node, rv)
		ReadProperties(node, rv)
		If node.HasAttribute(ATTR_OBJECT_NAME) Then rv.name = node.GetAttribute(ATTR_OBJECT_NAME)
		If node.HasAttribute(ATTR_OBJECT_NAME) Then rv.name = node.GetAttribute(ATTR_OBJECT_NAME)
		If node.HasAttribute(ATTR_OBJECT_TYPE) Then rv.objectType = node.GetAttribute(ATTR_OBJECT_TYPE)
		If node.HasAttribute(ATTR_OBJECT_TYPE) Then rv.objectType = node.GetAttribute(ATTR_OBJECT_TYPE)
		If node.HasAttribute(ATTR_OBJECT_X) Then rv.x = Int(node.GetAttribute(ATTR_OBJECT_X))
		If node.HasAttribute(ATTR_OBJECT_X) Then rv.x = Int(node.GetAttribute(ATTR_OBJECT_X))
		If node.HasAttribute(ATTR_OBJECT_Y) Then rv.y = Int(node.GetAttribute(ATTR_OBJECT_Y))
		If node.HasAttribute(ATTR_OBJECT_Y) Then rv.y = Int(node.GetAttribute(ATTR_OBJECT_Y))
		If node.HasAttribute(ATTR_OBJECT_WIDTH) Then rv.width = Int(node.GetAttribute(ATTR_OBJECT_WIDTH))
		If node.HasAttribute(ATTR_OBJECT_WIDTH) Then rv.width = Int(node.GetAttribute(ATTR_OBJECT_WIDTH))
		If node.HasAttribute(ATTR_OBJECT_HEIGHT) Then rv.height = Int(node.GetAttribute(ATTR_OBJECT_HEIGHT))
		If node.HasAttribute(ATTR_OBJECT_HEIGHT) Then rv.height = Int(node.GetAttribute(ATTR_OBJECT_HEIGHT))
		DoPostLoad(rv)
		DoPostLoad(rv)
		Return rv
		Return rv
	End
	End
	Method ReadTileData:TileMapData(node:XMLNode, layer:TileMapTileLayer)
	Method ReadTileData:TileMapData(node:XMLNode, layer:TileMapTileLayer)
		Local rv:TileMapData = tileMap.CreateData(layer.width, layer.height)
		Local rv:TileMapData = tileMap.CreateData(layer.width, layer.height)
		' default to raw xml (ugly)
		' default to raw xml (ugly)
		Local encoding$ = DATA_ENCODING_RAW
		Local encoding$ = DATA_ENCODING_RAW
		If node.HasAttribute(ATTR_DATA_ENCODING) Then encoding = node.GetAttribute(ATTR_DATA_ENCODING)
		If node.HasAttribute(ATTR_DATA_ENCODING) Then encoding = node.GetAttribute(ATTR_DATA_ENCODING)
		If encoding = DATA_ENCODING_RAW Then
		If encoding = DATA_ENCODING_RAW Then
			' TODO: raw xml
			' TODO: raw xml
			Error("TileMap: Raw xml is currently not supported")
			Error("TileMap: Raw xml is currently not supported")
		Elseif encoding = DATA_ENCODING_CSV Then
		Elseif encoding = DATA_ENCODING_CSV Then
			Local csv:String[] = node.value.Split(",")
			Local csv:String[] = node.value.Split(",")
			For Local i% = 0 Until csv.Length
			For Local i% = 0 Until csv.Length
				Local gid:Int = Int(csv[i].Trim())
				Local gid:Int = Int(csv[i].Trim())
				rv.tiles[i] = gid
				rv.tiles[i] = gid
				rv.cells[i] = tileMap.CreateCell(gid, i Mod rv.width, i / rv.width)
				rv.cells[i] = tileMap.CreateCell(gid, i Mod rv.width, i / rv.width)
			Next
			Next
		Elseif encoding = DATA_ENCODING_BASE64 Then
		Elseif encoding = DATA_ENCODING_BASE64 Then
			Local bytes:Int[] = DecodeBase64Bytes(node.value)
			Local bytes:Int[] = DecodeBase64Bytes(node.value)
			If node.HasAttribute(ATTR_DATA_COMPRESSION) Then
			If node.HasAttribute(ATTR_DATA_COMPRESSION) Then
				' TODO: compression
				' TODO: compression
				Error("TileMap: Compression is currently not supported")
				Error("TileMap: Compression is currently not supported")
			End
			End
			For Local i% = 0 Until bytes.Length Step 4
			For Local i% = 0 Until bytes.Length Step 4
				' little endian
				' little endian
				Local gid% = bytes[i]
				Local gid% = bytes[i]
				gid += bytes[i + 1] Shl 8
				gid += bytes[i + 1] Shl 8
				gid += bytes[i + 2] Shl 16
				gid += bytes[i + 2] Shl 16
				gid += bytes[i + 3] Shl 24
				gid += bytes[i + 3] Shl 24
				rv.tiles[i / 4] = gid
				rv.tiles[i / 4] = gid
				rv.cells[i / 4] = tileMap.CreateCell(gid, (i / 4) Mod rv.width, (i / 4) / rv.width)
				rv.cells[i / 4] = tileMap.CreateCell(gid, (i / 4) Mod rv.width, (i / 4) / rv.width)
			Next
			Next
		End
		End
		Return rv
		Return rv
	End
	End
End
End
' TileMapProperties
' TileMapProperties
' Container for properties.
' Container for properties.
Class TileMapProperties
Class TileMapProperties
	Field props:StringMap<TileMapProperty> = New StringMap<TileMapProperty>
	Field props:StringMap<TileMapProperty> = New StringMap<TileMapProperty>
	Method Has:Bool(name:String)
	Method Has:Bool(name:String)
		Return props.Contains(name)
		Return props.Contains(name)
	End
	End
	Method Get:TileMapProperty(name:String)
	Method Get:TileMapProperty(name:String)
		Return props.Get(name)
		Return props.Get(name)
	End
	End
	Method Set:Void(name:String, prop:TileMapProperty)
	Method Set:Void(name:String, prop:TileMapProperty)
		props.Set(name, prop)
		props.Set(name, prop)
	End
	End
End
End
' TileMap
' TileMap
' The main Map class.
' The main Map class.
Class TileMap Extends TileMapPropertyContainer Implements ITileMapPostLoad
Class TileMap Extends TileMapPropertyContainer Implements ITileMapPostLoad
	' attributes
	' attributes
	Field version:String = "1.0"
	Field version:String = "1.0"
	Field orientation:String = MAP_ORIENTATION_ORTHOGONAL
	Field orientation:String = MAP_ORIENTATION_ORTHOGONAL
	Field width:Int
	Field width:Int
	Field height:Int
	Field height:Int
	Field tileWidth:Int = 32
	Field tileWidth:Int = 32
	Field tileHeight:Int = 32
	Field tileHeight:Int = 32
	' children
	' children
	Field tilesets:StringMap<TileMapTileset> = New StringMap<TileMapTileset>
	Field tilesets:StringMap<TileMapTileset> = New StringMap<TileMapTileset>
	Field layers:Stack<TileMapLayer> = New Stack<TileMapLayer>
	Field layers:Stack<TileMapLayer> = New Stack<TileMapLayer>
	' post-load
	' post-load
	Field layerNames:StringMap<TileMapLayer> = New StringMap<TileMapLayer>
	Field layerNames:StringMap<TileMapLayer> = New StringMap<TileMapLayer>
	Field tiles:TileMapTile[]
	Field tiles:TileMapTile[]
	Field maxTileWidth:Int
	Field maxTileWidth:Int
	Field maxTileHeight:Int
	Field maxTileHeight:Int
	Field wrapX:Bool = False
	Field wrapX:Bool = False
	Field wrapY:Bool = False
	Field wrapY:Bool = False
	' optimisation
	' optimisation
	Field layerArray:TileMapLayer[] = []
	Field layerArray:TileMapLayer[] = []
	Field animatedTiles:TileMapTile[]
	Field animatedTiles:TileMapTile[]
	' summary: override this to do something before a layer is rendered
	' summary: override this to do something before a layer is rendered
	Method PreRenderLayer:Void(tileLayer:TileMapLayer)
	Method PreRenderLayer:Void(tileLayer:TileMapLayer)
		ConfigureLayer(tileLayer) ' call the deprecated one
		ConfigureLayer(tileLayer) ' call the deprecated one
	End
	End
	' summary: override this to do something after a layer is rendered
	' summary: override this to do something after a layer is rendered
	Method PostRenderLayer:Void(tileLayer:TileMapLayer)
	Method PostRenderLayer:Void(tileLayer:TileMapLayer)
	End
	End
	' summary: override this to do something before the map is rendered
	' summary: override this to do something before the map is rendered
	Method PreRenderMap:Void()
	Method PreRenderMap:Void()
	End
	End
	'summary: override this to do something after the map is rendered
	'summary: override this to do something after the map is rendered
	Method PostRenderMap:Void()
	Method PostRenderMap:Void()
	End
	End
	'summary: override this to configure a layer (called on every render) 'deprecated, override PreRenderLayer instead
	'summary: override this to configure a layer (called on every render) 'deprecated, override PreRenderLayer instead
	Method ConfigureLayer:Void(tileLayer:TileMapLayer)
	Method ConfigureLayer:Void(tileLayer:TileMapLayer)
	End
	End
	'summary: override this to draw a tile
	'summary: override this to draw a tile
	Method DrawTile:Void(tileLayer:TileMapTileLayer, mapTile:TileMapTile, x:Int, y:Int)
	Method DrawTile:Void(tileLayer:TileMapTileLayer, mapTile:TileMapTile, x:Int, y:Int)
		If mapTile.rawImage Then
		If mapTile.rawImage Then
			DrawImageRect(mapTile.rawImage, x, y, mapTile.srcX, mapTile.srcY, mapTile.width, mapTile.height, 0, 1, 1)
			DrawImageRect(mapTile.rawImage, x, y, mapTile.srcX, mapTile.srcY, mapTile.width, mapTile.height, 0, 1, 1)
		End
		End
	End
	End
	'summary: override this to create a custom tile class
	'summary: override this to create a custom tile class
	Method CreateTile:TileMapTile(id:Int)
	Method CreateTile:TileMapTile(id:Int)
		Return New TileMapTile(id)
		Return New TileMapTile(id)
	End
	End
	'summary: override this to create a custom tileset class
	'summary: override this to create a custom tileset class
	Method CreateTileset:TileMapTileset()
	Method CreateTileset:TileMapTileset()
		Return New TileMapTileset
		Return New TileMapTileset
	End
	End
	'summary: override this to create a custom tile layer class
	'summary: override this to create a custom tile layer class
	Method CreateTileLayer:TileMapTileLayer()
	Method CreateTileLayer:TileMapTileLayer()
		Return New TileMapTileLayer
		Return New TileMapTileLayer
	End
	End
	'summary: override this to create a custom object layer class
	'summary: override this to create a custom object layer class
	Method CreateObjectLayer:TileMapObjectLayer()
	Method CreateObjectLayer:TileMapObjectLayer()
		Return New TileMapObjectLayer
		Return New TileMapObjectLayer
	End
	End
	'summary: override this to create a custom image class
	'summary: override this to create a custom image class
	Method CreateImage:TileMapImage()
	Method CreateImage:TileMapImage()
		Return New TileMapImage
		Return New TileMapImage
	End
	End
	'summary: override this to create a custom object class
	'summary: override this to create a custom object class
	Method CreateObject:TileMapObject()
	Method CreateObject:TileMapObject()
		Return New TileMapObject
		Return New TileMapObject
	End
	End
	'summary: override this to create a custom cell class
	'summary: override this to create a custom cell class
	Method CreateCell:TileMapCell(gid:Int, x:Int, y:Int)
	Method CreateCell:TileMapCell(gid:Int, x:Int, y:Int)
		Return New TileMapCell(gid, x, y)
		Return New TileMapCell(gid, x, y)
	End
	End
	'summary: override this to create a custom data class
	'summary: override this to create a custom data class
	Method CreateData:TileMapData(width:Int, height:Int)
	Method CreateData:TileMapData(width:Int, height:Int)
		Return New TileMapData(width, height)
		Return New TileMapData(width, height)
	End
	End
	'summary: override this to perform additional post-loading functionality (remember to call Super.PostLoad() first)
	'summary: override this to perform additional post-loading functionality (remember to call Super.PostLoad() first)
	Method PostLoad:Void()
	Method PostLoad:Void()
		Local totaltiles% = 0, ts:TileMapTileset
		Local totaltiles% = 0, ts:TileMapTileset
		Local alltiles:Stack<TileMapTile> = New Stack<TileMapTile>
		Local alltiles:Stack<TileMapTile> = New Stack<TileMapTile>
		For Local ts:TileMapTileset = EachIn tilesets.Values()
		For Local ts:TileMapTileset = EachIn tilesets.Values()
				'TODO:  Add an option here to try to load from a prefetch atlas asset instead.
				'TODO:  Add an option here to try to load from a prefetch atlas asset instead.
				'For now, load it with LoadImage
				'For now, load it with LoadImage
				ts.rawImage = LoadImage(ts.imageNode.source)
				ts.rawImage = LoadImage(ts.imageNode.source)
				If ts.rawImage Then
				If ts.rawImage Then
					ts.tileCountX = (ts.rawImage.Width() - ts.margin) / (ts.tileWidth + ts.spacing)
					ts.tileCountX = (ts.rawImage.Width() - ts.margin) / (ts.tileWidth + ts.spacing)
					ts.tileCountY = (ts.rawImage.Height() - ts.margin) / (ts.tileHeight + ts.spacing)
					ts.tileCountY = (ts.rawImage.Height() - ts.margin) / (ts.tileHeight + ts.spacing)
					ts.tileCount = ts.tileCountX * ts.tileCountY
					ts.tileCount = ts.tileCountX * ts.tileCountY
				Else
				Else
					Print "Couldn't find tileset image: "+ts.imageNode.source
					Print("Tilemap: WARNING, Couldn't find tileset image: " + ts.imageNode.source)
					Return
					Return
				End
				End
			' update max tile size
			' update max tile size
			If maxTileWidth < ts.tileWidth Then maxTileWidth = ts.tileWidth
			If maxTileWidth < ts.tileWidth Then maxTileWidth = ts.tileWidth
			If maxTileHeight < ts.tileHeight Then maxTileHeight = ts.tileHeight
			If maxTileHeight < ts.tileHeight Then maxTileHeight = ts.tileHeight
			' build tile list
			' build tile list
			ts.tiles = New TileMapTile[ts.tileCount]
			ts.tiles = New TileMapTile[ts.tileCount]
			For Local t:TileMapTile = Eachin ts.tileNodes
			For Local t:TileMapTile = Eachin ts.tileNodes
				ts.tiles[t.id] = t
				ts.tiles[t.id] = t
			Next
			Next
			For Local i% = 0 Until ts.tiles.Length
			For Local i% = 0 Until ts.tiles.Length
				If ts.tiles[i] = Null Then
				If ts.tiles[i] = Null Then
					ts.tiles[i] = CreateTile(i)
					ts.tiles[i] = CreateTile(i)
				End
				End
				ts.tiles[i].gid = ts.firstGid + i
				ts.tiles[i].gid = ts.firstGid + i
'				ts.tiles[i].image = ts.image
'				ts.tiles[i].image = ts.image
				ts.tiles[i].rawImage = ts.rawImage
				ts.tiles[i].rawImage = ts.rawImage
				ts.tiles[i].width = ts.tileWidth
				ts.tiles[i].width = ts.tileWidth
				ts.tiles[i].height = ts.tileHeight
				ts.tiles[i].height = ts.tileHeight
				' if we're using the raw image we need to precache some stuff
				' if we're using the raw image we need to precache some stuff
				If ts.rawImage Then
				If ts.rawImage Then
					ts.tiles[i].srcX = ts.margin + (ts.tileWidth + ts.spacing) * (i Mod ts.tileCountX)
					ts.tiles[i].srcX = ts.margin + (ts.tileWidth + ts.spacing) * (i Mod ts.tileCountX)
					ts.tiles[i].srcY = ts.margin + (ts.tileHeight + ts.spacing) * (i / ts.tileCountX)
					ts.tiles[i].srcY = ts.margin + (ts.tileHeight + ts.spacing) * (i / ts.tileCountX)
				End
				End
				alltiles.Push(ts.tiles[i])
				alltiles.Push(ts.tiles[i])
			Next
			Next
			' update total tiles
			' update total tiles
			totaltiles += ts.tileCount
			totaltiles += ts.tileCount
		Next
		Next
		' make our cache
		' make our cache
		tiles = New TileMapTile[totaltiles]
		tiles = New TileMapTile[totaltiles]
		For Local t:TileMapTile = Eachin alltiles
		For Local t:TileMapTile = Eachin alltiles
			tiles[t.gid - 1] = t
			tiles[t.gid - 1] = t
		Next
		Next
		' calculate the max tile size per layer
		' calculate the max tile size per layer
		For Local l:TileMapLayer = Eachin layers
		For Local l:TileMapLayer = Eachin layers
			If TileMapTileLayer(l) <> Null Then
			If TileMapTileLayer(l) <> Null Then
				Local tl:TileMapTileLayer = TileMapTileLayer(l)
				Local tl:TileMapTileLayer = TileMapTileLayer(l)
				For Local i% = 0 Until tl.mapData.tiles.Length
				For Local i% = 0 Until tl.mapData.tiles.Length
					If tl.mapData.tiles[i] > 0 Then
					If tl.mapData.tiles[i] > 0 Then
						If tl.maxTileWidth < tiles[tl.mapData.tiles[i] - 1].width Then tl.maxTileWidth = tiles[tl.mapData.tiles[i] - 1].width
						If tl.maxTileWidth < tiles[tl.mapData.tiles[i] - 1].width Then tl.maxTileWidth = tiles[tl.mapData.tiles[i] - 1].width
						If tl.maxTileHeight < tiles[tl.mapData.tiles[i] - 1].height Then tl.maxTileHeight = tiles[tl.mapData.tiles[i] - 1].height
						If tl.maxTileHeight < tiles[tl.mapData.tiles[i] - 1].height Then tl.maxTileHeight = tiles[tl.mapData.tiles[i] - 1].height
					End
					End
				Next
				Next
			End
			End
		Next
		Next
	End
	End
	Method GetAllObjects:Stack<TileMapObject>()
	Method GetAllObjects:Stack<TileMapObject>()
		Local rv:Stack<TileMapObject> = New Stack<TileMapObject>
		Local rv:Stack<TileMapObject> = New Stack<TileMapObject>
		For Local layer:TileMapLayer = EachIn layers
		For Local layer:TileMapLayer = EachIn layers
			If TileMapObjectLayer(layer) <> Null Then
			If TileMapObjectLayer(layer) <> Null Then
				For Local obj:TileMapObject = EachIn TileMapObjectLayer(layer).objects
				For Local obj:TileMapObject = EachIn TileMapObjectLayer(layer).objects
					rv.Push(obj)
					rv.Push(obj)
				Next
				Next
			End
			End
		Next
		Next
		Return rv
		Return rv
	End
	End
	Method FindObjectByName:TileMapObject(name:String)
	Method FindObjectByName:TileMapObject(name:String)
		For Local layer:TileMapLayer = EachIn layers
		For Local layer:TileMapLayer = EachIn layers
			If TileMapObjectLayer(layer) <> Null Then
			If TileMapObjectLayer(layer) <> Null Then
				For Local obj:TileMapObject = EachIn TileMapObjectLayer(layer).objects
				For Local obj:TileMapObject = EachIn TileMapObjectLayer(layer).objects
					If obj.name = name Then Return obj
					If obj.name = name Then Return obj
				Next
				Next
			EndIf
			EndIf
		Next
		Next
		Return Null
		Return Null
	End
	End
	Method RenderLayer:Void(layerName:String, bx:Int, by:Int, bw:Int, bh:Int, sx:Float = 1, sy:Float = 1, offsetX:Float = 0, offsetY:Float = 0)
	Method RenderLayer:Void(layerName:String, bx:Int, by:Int, bw:Int, bh:Int, sx:Float = 1, sy:Float = 1, offsetX:Float = 0, offsetY:Float = 0)
		Local layer:TileMapLayer
		Local layer:TileMapLayer
		For Local l:TileMapLayer = EachIn layers
		For Local l:TileMapLayer = EachIn layers
			If l.name.ToUpper() = layerName.ToUpper()
			If l.name.ToUpper() = layerName.ToUpper()
				layer = l
				layer = l
				Exit
				Exit
			End
			End
		End
		End
		RenderLayer(layer, bx, by, bw, bh, sx, sy, offsetX, offsetY)
		RenderLayer(layer, bx, by, bw, bh, sx, sy, offsetX, offsetY)
	End
	End
	' bx,by: top-left corner of the viewport
	' bx,by: top-left corner of the viewport
	' bw,bh: dimensions of the viewport
	' bw,bh: dimensions of the viewport
	' sx,xy: scale when rendering tiles
	' sx,xy: scale when rendering tiles
	' offsetX,offsetY: the point in the UNSCALED map that should appear in the top-left corner
	' offsetX,offsetY: the point in the UNSCALED map that should appear in the top-left corner
	' tiles will be rendered as if the target location were 0,0 in the viewport, and the scaling handle is at offsetX, offsetY
	' tiles will be rendered as if the target location were 0,0 in the viewport, and the scaling handle is at offsetX, offsetY
	 Method RenderLayer:Void(layer:TileMapLayer, bx:Int, by:Int, bw:Int, bh:Int, sx:Float = 1, sy:Float = 1, offsetX:Float = 0, offsetY:Float = 0)
	 Method RenderLayer:Void(layer:TileMapLayer, bx:Int, by:Int, bw:Int, bh:Int, sx:Float = 1, sy:Float = 1, offsetX:Float = 0, offsetY:Float = 0)
		If layer.visible And TileMapTileLayer(layer) <> Null Then
		If layer.visible And TileMapTileLayer(layer) <> Null Then
			Local tl:TileMapTileLayer = TileMapTileLayer(layer)
			Local tl:TileMapTileLayer = TileMapTileLayer(layer)
			'Local mapTile:TileMapTile, gid%
			'Local mapTile:TileMapTile, gid%
			PreRenderLayer(layer)
			PreRenderLayer(layer)
			PushMatrix()
			PushMatrix()
			Translate(bx, by)
			Translate(bx, by)
			Scale(sx, sy)
			Scale(sx, sy)
			' ortho
			' ortho
			If orientation = MAP_ORIENTATION_ORTHOGONAL Then
			If orientation = MAP_ORIENTATION_ORTHOGONAL Then
				' size of the scaled tiles
				' size of the scaled tiles
				Local scaledTileWidth:Float = tl.maxTileWidth * sx
				Local scaledTileWidth:Float = tl.maxTileWidth * sx
				Local scaledTileHeight:Float = tl.maxTileHeight * sy
				Local scaledTileHeight:Float = tl.maxTileHeight * sy
				' the range of tiles that are visible, based on the scale and parallax
				' the range of tiles that are visible, based on the scale and parallax
				Local startMapX:Int = (offsetX * tl.parallaxScaleX / tl.maxTileWidth) - 1
				Local startMapX:Int = (offsetX * tl.parallaxScaleX / tl.maxTileWidth) - 1
				Local startMapY:Int = (offsetY * tl.parallaxScaleY / tl.maxTileHeight) - 1
				Local startMapY:Int = (offsetY * tl.parallaxScaleY / tl.maxTileHeight) - 1
				Local endMapX:Int = startMapX + (bw / scaledTileWidth) + 2
				Local endMapX:Int = startMapX + (bw / scaledTileWidth) + 2
				Local endMapY:Int = startMapY + (bh / scaledTileHeight) + 2
				Local endMapY:Int = startMapY + (bh / scaledTileHeight) + 2
				'Print "x,y,x,y="+startMapX+","+startMapY+","+endMapX+","+endMapY
				'Print "x,y,x,y="+startMapX+","+startMapY+","+endMapX+","+endMapY
				' loop on potentially visible tiles (y direction)
				' loop on potentially visible tiles (y direction)
				For Local my:Int = startMapY To endMapY
				For Local my:Int = startMapY To endMapY
					Local my2:Int = my
					Local my2:Int = my
					' wrap y direction if necessary
					' wrap y direction if necessary
					If wrapY Then
					If wrapY Or tl.wrapY Then
						While my2 < 0; my2 += height; End
						While my2 < 0; my2 += height; End
						While my2 >= height; my2 -= height; End
						While my2 >= height; my2 -= height; End
					End
					End
					' loop on potentially visible tiles (x direction)
					' loop on potentially visible tiles (x direction)
					For Local mx:Int = startMapX To endMapX
					For Local mx:Int = startMapX To endMapX
						Local mx2:Int = mx
						Local mx2:Int = mx
						' wrap x direction if necessary
						' wrap x direction if necessary
						If wrapX Then
						If wrapX Or tl.wrapX Then
							While mx2 < 0; mx2 += width; End
							While mx2 < 0; mx2 += width; End
							While mx2 >= width; mx2 -= width; End
							While mx2 >= width; mx2 -= width; End
						End
						End
						' check the range
						' check the range
						If mx2 >= 0 And mx2 < width And my2 >= 0 And my2 < height Then
						If mx2 >= 0 And mx2 < width And my2 >= 0 And my2 < height Then
							' get the global id
							' get the global id
							Local gid:Int = tl.mapData.cells[mx2 + my2 * tl.mapData.width].gid
							Local gid:Int = tl.mapData.cells[mx2 + my2 * tl.mapData.width].gid
							' if we have an id
							' if we have an id
							If gid > 0 Then
							If gid > 0 Then
								' get the tile
								' get the tile
								Local mapTile:TileMapTile = tiles[gid - 1]
								Local mapTile:TileMapTile = tiles[gid - 1]
								' work out the render position based on tile sizes, parallax, and offset
								' work out the render position based on tile sizes, parallax, and offset
								' rendering scale is handled automatically by the transformation matrix
								' rendering scale is handled automatically by the transformation matrix
								Local rx:Int = mx * tl.maxTileWidth - offsetX * tl.parallaxScaleX
								Local rx:Int = mx * tl.maxTileWidth - offsetX * tl.parallaxScaleX
 								Local ry:Int = my * tl.maxTileHeight - offsetY * tl.parallaxScaleY
 								Local ry:Int = my * tl.maxTileHeight - offsetY * tl.parallaxScaleY
								DrawTile(tl, mapTile, rx, ry)
								DrawTile(tl, mapTile, rx, ry)
							End
							End
						End
						End
					Next
					Next
				Next
				Next
			' iso
			' iso
			Elseif orientation = MAP_ORIENTATION_ISOMETRIC Then
			Elseif orientation = MAP_ORIENTATION_ISOMETRIC Then
				' TODO: wrapping
				' TODO: wrapping
				For Local y:Int = 0 Until tl.width + tl.height
				For Local y:Int = 0 Until tl.width + tl.height
					Local ry:Int = y
					Local ry:Int = y
					Local rx:Int = 0
					Local rx:Int = 0
					While ry >= tl.height
					While ry >= tl.height
						ry -= 1
						ry -= 1
						rx += 1
						rx += 1
					Wend
					Wend
					While ry >= 0 And rx < tl.width
					While ry >= 0 And rx < tl.width
						Local gid:Int = tl.mapData.cells[rx + ry * tl.mapData.width].gid
						Local gid:Int = tl.mapData.cells[rx + ry * tl.mapData.width].gid
						If gid > 0 Then
						If gid > 0 Then
							Local mapTile:TileMapTile = tiles[gid - 1]
							Local mapTile:TileMapTile = tiles[gid - 1]
							DrawTile(tl, mapTile, (rx - ry - 1) * tileWidth / 2 - bx, (rx + ry + 2) * tileHeight / 2 - mapTile.height - by)
							DrawTile(tl, mapTile, (rx - ry - 1) * tileWidth / 2 - bx, (rx + ry + 2) * tileHeight / 2 - mapTile.height - by)
						Endif
						Endif
						ry -= 1
						ry -= 1
						rx += 1
						rx += 1
					End
					End
				Next
				Next
			End
			End
			PopMatrix()
			PopMatrix()
			PostRenderLayer(layer)
			PostRenderLayer(layer)
		End
		End
	End
	End
	' bx,by,bw,bh = render bounds (screen)
	' bx,by,bw,bh = render bounds (screen)
	' sx,sy = scale x/y (float, defaults to 1) i'll do this later
	' sx,sy = scale x/y (float, defaults to 1) i'll do this later
	' wx,wy = wrap x/y (boolean, defaults to false)
	' wx,wy = wrap x/y (boolean, defaults to false)
	Method RenderMap:Void(bx:Int, by:Int, bw:Int, bh:Int, sx:Float = 1, sy:Float = 1, offsetX:Float = 0, offsetY:Float = 0, layerName:String = "ALL", scissor:Bool = True)
	Method RenderMap:Void(bx:Int, by:Int, bw:Int, bh:Int, sx:Float = 1, sy:Float = 1, offsetX:Float = 0, offsetY:Float = 0, layerName:String = "ALL", scissor:Bool = True)
		PreRenderMap()
		PreRenderMap()
		If scissor Then
		If scissor Then
			Local matrix:Float[] = GetMatrix()
			Local matrix:Float[] = GetMatrix()
			SetScissor(bx * matrix[0] + matrix[4], by * matrix[3] + matrix[5], bw * matrix[0], bh * matrix[3])
			SetScissor(bx * matrix[0] + matrix[4], by * matrix[3] + matrix[5], bw * matrix[0], bh * matrix[3])
		End If
		End If
		If layerName = "ALL" Then
		If layerName = "ALL" Then
			For Local layer:TileMapLayer = EachIn layers
			For Local layer:TileMapLayer = EachIn layers
				RenderLayer(layer, bx, by, bw, bh, sx, sy, offsetX, offsetY)
				RenderLayer(layer, bx, by, bw, bh, sx, sy, offsetX, offsetY)
			Next
			Next
		Else
		Else
			Local layer:TileMapLayer
			Local layer:TileMapLayer
			For Local l:TileMapLayer = EachIn layers
			For Local l:TileMapLayer = EachIn layers
				If l.name.ToUpper() = layerName.ToUpper()
				If l.name.ToUpper() = layerName.ToUpper()
					layer = l
					layer = l
					Exit
					Exit
				End
				End
			End
			End
			RenderLayer(layer, bx, by, bw, bh, sx, sy, offsetX, offsetY)
			RenderLayer(layer, bx, by, bw, bh, sx, sy, offsetX, offsetY)
		End
		End
		If scissor Then SetScissor(0, 0, DeviceWidth(), DeviceHeight())
		If scissor Then SetScissor(0, 0, DeviceWidth(), DeviceHeight())
		PostRenderMap()
		PostRenderMap()
	End
	End
	Method GetBounds:TileMapRect()
	Method GetBounds:TileMapRect()
		Local rv:TileMapRect = New TileMapRect
		Local rv:TileMapRect = New TileMapRect
		If orientation = MAP_ORIENTATION_ORTHOGONAL Then
		If orientation = MAP_ORIENTATION_ORTHOGONAL Then
			rv.x = 0
			rv.x = 0
			rv.y = tileHeight - maxTileHeight
			rv.y = tileHeight - maxTileHeight
			rv.w = (width - 1) * tileWidth + maxTileWidth
			rv.w = (width - 1) * tileWidth + maxTileWidth
			rv.h = (height - 1) * tileHeight + maxTileHeight
			rv.h = (height - 1) * tileHeight + maxTileHeight
		Elseif orientation = MAP_ORIENTATION_ISOMETRIC Then
		Elseif orientation = MAP_ORIENTATION_ISOMETRIC Then
			rv.x = -height * tileWidth / 2
			rv.x = -height * tileWidth / 2
			rv.y = tileHeight - maxTileHeight
			rv.y = tileHeight - maxTileHeight
			rv.w = (width - 2) * tileWidth / 2 + maxTileWidth - rv.x
			rv.w = (width - 2) * tileWidth / 2 + maxTileWidth - rv.x
			rv.h = (width + height) * tileHeight / 2 - rv.y
			rv.h = (width + height) * tileHeight / 2 - rv.y
		Endif
		Endif
		Return rv
		Return rv
	End
	End
	Method UpdateAnimation:Void(timePassed:Int)
	Method UpdateAnimation:Void(timePassed:Int)
		Local layer:TileMapLayer, tl:TileMapTileLayer, cell:TileMapCell, t:TileMapTile
		Local layer:TileMapLayer, tl:TileMapTileLayer, cell:TileMapCell, t:TileMapTile
		Local cellCount:Int, i:Int, j:Int
		Local cellCount:Int, i:Int, j:Int
		' get the layers as an array
		' get the layers as an array
		Local layerCount:Int = layers.Length()
		Local layerCount:Int = layers.Length()
		If layerArray.Length < layerCount Then
		If layerArray.Length < layerCount Then
			layerArray = layers.ToArray()
			layerArray = layers.ToArray()
			layerCount = layerArray.Length
			layerCount = layerArray.Length
		Else
		Else
			'layerCount = layers.FillArray(layerArray)  'Replaced:  See below 3 lines -nobu
			'layerCount = layers.FillArray(layerArray)  'Replaced:  See below 3 lines -nobu
			For Local k:Int = 0 Until layers.Length
			For Local k:Int = 0 Until layers.Length
				layers.Data[k] = layerArray[k]
				layers.Data[k] = layerArray[k]
			Next
			Next
		End
		End
		' loop on each layer
		' loop on each layer
		For i = 0 Until layerCount
		For i = 0 Until layerCount
			' cast
			' cast
			tl = TileMapTileLayer(layerArray[i])
			tl = TileMapTileLayer(layerArray[i])
			' if the layer is a tile layer
			' if the layer is a tile layer
			If tl <> Null Then
			If tl <> Null Then
				' loop on each cell
				' loop on each cell
				cellCount = tl.mapData.cells.Length
				cellCount = tl.mapData.cells.Length
				For j = 0 Until cellCount
				For j = 0 Until cellCount
					cell = tl.mapData.cells[j]
					cell = tl.mapData.cells[j]
					' if the cell exists and has a value
					' if the cell exists and has a value
					If cell <> Null And cell.gid > 0 Then
					If cell <> Null And cell.gid > 0 Then
						' get the tile
						' get the tile
						t = tiles[cell.gid-1]
						t = tiles[cell.gid-1]
						' if the direction is 0 (paused), do nothing
						' if the direction is 0 (paused), do nothing
						If t <> Null And cell.direction <> 0 Then
						If t <> Null And cell.direction <> 0 Then
							' add our time to the time passed for the cell
							' add our time to the time passed for the cell
							cell.timePassed += timePassed
							cell.timePassed += timePassed
							' if this tile has animation information
							' if this tile has animatio
							If t.animated Then
								' get the new direction if we have one
								If t.hasAnimDirection Then cell.direction = t.animDirection
								' while it's not paused and we're not up to the current frame
								While cell.direction <> 0 And cell.timePassed >= t.animDelay
									' move to the next frame
									cell.timePassed -= t.animDelay
									cell.gid += cell.direction * t.animNext
									' get the tile for the new frame
									t = tiles[cell.gid-1]
									' if there's animation information, get it
									If t <> Null And t.animated T