├── .gitignore ├── Activators.png ├── Ammo.png ├── Arrows ├── Arrows.idraw └── Arrows.png ├── DeathMatchMap.svg ├── DocOptTrial.py ├── DoorActivators.png ├── Doors.png ├── Floors.png ├── Healing.png ├── MapDataExtractor.py ├── MapTiler.py ├── NavData.csv ├── Portals.png ├── README.md ├── Screen Shot 2015-01-05 at 6.42.20 PM.png ├── Transformers Movie.ttf ├── Transformers font.txt ├── Walls.png ├── Weapons.png ├── docopt.py ├── tiled.tmx └── tileset.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/.name 3 | 4 | .idea/encodings.xml 5 | 6 | *.xml 7 | 8 | *.iml 9 | 10 | *.pyc 11 | 12 | DeathMatchMap.idraw 13 | -------------------------------------------------------------------------------- /Activators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Activators.png -------------------------------------------------------------------------------- /Ammo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Ammo.png -------------------------------------------------------------------------------- /Arrows/Arrows.idraw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Arrows/Arrows.idraw -------------------------------------------------------------------------------- /Arrows/Arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Arrows/Arrows.png -------------------------------------------------------------------------------- /DocOptTrial.py: -------------------------------------------------------------------------------- 1 | """Usage: DocOptTrial.py [--p=VALUE ...] 2 | """ 3 | 4 | import docopt 5 | 6 | arguments = docopt.docopt(__doc__) 7 | # When testing is done, this is where 8 | # test arguments are inserted. 9 | 10 | print "-----------------------------------" 11 | print "Inputs:" 12 | args = arguments.keys() 13 | args.sort() 14 | for arg in args: 15 | print "%-25s %s" % (arg, arguments[arg]) 16 | print "-----------------------------------" -------------------------------------------------------------------------------- /DoorActivators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/DoorActivators.png -------------------------------------------------------------------------------- /Doors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Doors.png -------------------------------------------------------------------------------- /Floors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Floors.png -------------------------------------------------------------------------------- /Healing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Healing.png -------------------------------------------------------------------------------- /MapDataExtractor.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------- 2 | # License 3 | # ------------------------------------------------------------- 4 | # Permission is hereby granted, free of charge, to any person 5 | # obtaining a copy of this software and associated 6 | # documentation files (the "Software"), to deal in the Software 7 | # without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to 10 | # whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall 14 | # be included in all copies or substantial portions of the 15 | # Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 18 | # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 19 | # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 20 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | # ------------------------------------------------------------- 26 | 27 | 28 | """ 29 | Post process a Tiled (.tmx) file to generate navigation and object information 30 | for a game. This is part of a specific work flow where: 31 | 32 | 1. The map is generated in layers. 33 | 2. Some specific layers are present by design, if not actual name: 34 | Floors - Specifies all the floor tiles in the map. 35 | Doors - Specifies door locations. Doors go across edges. 36 | Walls - Walls block movement into a tile. 37 | 3. All other layers are treated as "Objects" unless specifically excluded (excludelayers) 38 | or handled explicitly (e.g. DoorActivators). 39 | When the processing is done, a file with all the objects in it will be created. 40 | 4. A Rooms layer has been created identifying the tiles for specific rooms. 41 | 42 | 43 | 44 | This script is meant to run against the output of MapTiler.py. 45 | 46 | This script will (optionally controlled) generate the following outputs: 47 | 48 | NOTES: 49 | 1. You can specify the --exclude option to remove any layers from consideration 50 | for objects. The --navPrefix specified layers will not be considered automatically, 51 | as well as the floor and wall layers. The doors layer is used to create objects. 52 | 2. Objects found are considered to not block entry into the cell for the purpose of generating 53 | adjacency information. You can specify an object as blocking by using the --blocking option 54 | for that layer. This usage is highly game engine specific (i.e. if your agent wants to navigate 55 | to the object that is considered "blocked", then they have to navigate to an adjacent cell. Your 56 | engine has to handle this). 57 | 3. An adjancency list (edges) for all the walkable nodes will be generated. This will take into account 58 | walls, blocking objects, and doors. If the edge exists, it will be marked indicating the type of 59 | action needed to traverse the edge. This could be more sophisticated, but for now, you can either 60 | walk an edge in a room, wall through a portal between rooms, or walk through a door between rooms. 61 | Edge Markings: 62 | WALK - Just walk from cell A to cell B. 63 | DOOR - To go from cell A to cell B, you must go through a door. 64 | ROOM - Walk between two rooms, but without going through a door. This is a portal boundary. 65 | 66 | Usage: MapDataExtractor.py 67 | [--floorLayer=FLOORS] 68 | [--wallLayer=WALLS] 69 | [--doorLayer=DOORS] 70 | [--navPrefix=NAVPREFIX] 71 | [--outFile=OUTFILE] 72 | [--verbose] 73 | [--exclude=LAYER ...] 74 | [--blocking=LAYER ...] 75 | [--binding=LAYER ...] 76 | [--noDiagonalEdges] 77 | [--reallyVerbose] 78 | Argumnts: 79 | tiledFile The Tiled (.tmx) file that contains the Tiled data. 80 | 81 | Options: 82 | --wallLayer=WALLS The name of the layer used for walls. 83 | [Default: Walls] 84 | --floorLayer=FLOORS The name of the layer used for floors. 85 | [Default: Floors] 86 | --doorLayer=DOORS The name of the layer used for doors. 87 | [Default: Doors] 88 | --navPrefix=NAVPREFIX The prefix used for navigation layers generated or manually created. 89 | [Default: NAV_] 90 | --outFile=OUTFILE The CSV output file with all the output data. See below for format. 91 | [Default: NavData.csv] 92 | --verbose Generate output while processing. 93 | --reallyVerbose Generate even more output while processing. 94 | --exclude=LAYER Specifies a layer to be excluded from processing. 95 | Can be specified multiple times. 96 | --blocking=LAYER Specifies an object layer that will be considered as 97 | blocking in navigation graph generation. 98 | Can be specified multiple times. 99 | --binding=LAYER Specifies an object layer that will be considered as 100 | some kind of "activator" that needs to be bound to the 101 | nearest object (other than something that binds). This 102 | is useful for creating door activators, etc. 103 | Can be specified multiple times. 104 | --noDiagonalEdges By default, the adjacent check will consider 105 | diagonal edges as well. This option restricts 106 | the check to only N, S, E, W checks. 107 | 108 | Website: http://www.NonlinearIdeas.com 109 | Repository: https://github.com/NonlinearIdeas/Map-Tiler 110 | Report Issues: https://github.com/NonlinearIdeas/Map-Tiler 111 | License: MIT License - See specific text in source code. 112 | Copyright: Copyright (c) 2015 Nonlinear Ideas Inc (contact@nlideas.com) 113 | """ 114 | 115 | import os 116 | from lxml import etree 117 | import docopt 118 | import csv 119 | import string 120 | 121 | class MapDataExtractor(object): 122 | # Keys used for holding output data 123 | KEY_WALKABLE = "Walkable" 124 | KEY_ROOMS = "Rooms" 125 | KEY_PROPERTY_ROOM = "ROOM" 126 | KEY_OBJECTS = "Objects" 127 | KEY_ADJACENCY = "Adjacency" 128 | KEY_EDGE_ROOM = "ROOM" 129 | KEY_EDGE_DOOR = "DOOR" 130 | KEY_EDGE_WALK = "WALK" 131 | CSV_EXPORT_COLUMNS = 10 132 | 133 | def __init__(self): 134 | self.Reset() 135 | 136 | def ExtractTileIndex(self, gid): 137 | FLAGS = 0x80000000 | 0x40000000 | 0x20000000 138 | return gid & ~FLAGS 139 | 140 | def CalculateCellIndex(self, col, row): 141 | index = row * self.layerWidth + col 142 | return index 143 | 144 | def CalculateCellColRow(self, index): 145 | row = index / self.layerWidth 146 | col = index % self.layerWidth 147 | return col, row 148 | 149 | def Reset(self): 150 | # Tile width and height in pixels. 151 | self.tileWidth = 0 152 | self.tileHeight = 0 153 | 154 | # Layer width and height in tiles. 155 | self.layerWidth = 0 156 | self.layerHeight = 0 157 | 158 | # The number of tiles total for a layer. 159 | # This is the width x height. 160 | self.layerTiles = 0 161 | 162 | # Keyed by the tile index. Contains a dictionary 163 | # of properties for each tile gleaned from the Tiled file. 164 | self.tileMap = {} 165 | 166 | # Keyed by the layer name. Each entry contains a list of the tile 167 | # indexes in that layer. An index of 0 indicates the tile is 168 | # unpopulated. 169 | self.layerMap = {} 170 | 171 | # Original file used in the tile set. 172 | self.tileMapFile = "" 173 | 174 | # All output results are stored in this dictionary by 175 | # key. Each object stored may be a different format. 176 | self.outputDict = {} 177 | 178 | def FatalError(self,message): 179 | print message 180 | print "Unable to continue." 181 | return False 182 | 183 | def LoadTiledMap(self,tiledFile): 184 | # Does it exist? 185 | if not os.path.exists(tiledFile): 186 | return self.FatalError("Tiled file %s does not exist."%tiledFile) 187 | 188 | # Load the existing file. 189 | inTree = etree.parse(tiledFile) 190 | inRoot = inTree.getroot() 191 | 192 | # Basic parameters 193 | self.tileWidth = int(inRoot.attrib["tilewidth"]) 194 | self.tileHeight = int(inRoot.attrib["tileheight"]) 195 | self.layerWidth = int(inRoot.attrib["width"]) 196 | self.layerHeight = int(inRoot.attrib["height"]) 197 | self.layerTiles = self.layerWidth*self.layerHeight 198 | 199 | # Tileset Information 200 | self.tileMap = { idx:{} for idx in xrange(self.layerTiles)} 201 | for tileset in inRoot.findall("tileset"): 202 | if tileset.attrib["firstgid"] == "1": 203 | self.tileMapFile = tileset.find("image").attrib["source"] 204 | for tile in tileset.findall("tile"): 205 | id = int(tile.attrib["id"]) 206 | for properties in tile.findall("properties"): 207 | for property in properties.findall("property"): 208 | self.tileMap[id][property.attrib["name"]] = property.attrib["value"] 209 | 210 | # Layers 211 | self.layerMap = {layer.attrib['name']: {} for layer in inRoot.findall("layer")} 212 | for layer in inRoot.findall("layer"): 213 | tiles = layer.find("data").findall("tile") 214 | for idx in xrange(len(tiles)): 215 | tile = tiles[idx] 216 | tileID = self.ExtractTileIndex(int(tile.attrib["gid"])) 217 | if tileID > 0: 218 | self.layerMap[layer.attrib['name']][idx] = tileID-1 219 | return True 220 | 221 | def FormatNavName(self,name): 222 | return self.navPrefix + name 223 | 224 | # Given a layer index, find the indices for the cells that 225 | # are adjacent to it. 226 | def GetAdjacentIndexes(self,layerIndex): 227 | col, row = self.CalculateCellColRow(layerIndex) 228 | adjacent = [ self.CalculateCellIndex(col + 1, row + 0), 229 | self.CalculateCellIndex(col - 1, row + 0), 230 | self.CalculateCellIndex(col + 0, row + 1), 231 | self.CalculateCellIndex(col + 0, row - 1), 232 | ] 233 | if self.diagonalEdges: 234 | adjacent += [self.CalculateCellIndex(col + 1, row + 1), 235 | self.CalculateCellIndex(col + 1, row - 1), 236 | self.CalculateCellIndex(col - 1, row + 1), 237 | self.CalculateCellIndex(col - 1, row - 1)] 238 | return adjacent 239 | 240 | # Perform a search on the adjacent cells until we run out of cells 241 | # that are connected. This is used to find all the cells associated 242 | # with a particular object. 243 | # NOTE: This function will not remove the found indices from the 244 | # original passed in, it will use a local copy. 245 | def FindConnectedIndices(self,layerIndex,indices): 246 | # Make a local copy 247 | indices = indices[:] 248 | # Nothing in the result yet. 249 | result = [] 250 | # Prime the pump 251 | queue = [layerIndex] 252 | # Start the pump 253 | while len(queue) > 0: 254 | next = queue.pop(0) 255 | result.append(next) 256 | # Remove from future consideration. 257 | if next in indices: 258 | indices.remove(next) 259 | adjacent = self.GetAdjacentIndexes(next) 260 | for adj in adjacent: 261 | # This is the most common case so it gets 262 | # checked first. Avoids the other checks 263 | # most of the time. 264 | if adj not in indices: 265 | continue 266 | if adj in queue: 267 | continue 268 | if adj in result: 269 | continue 270 | # Must be a keeper 271 | queue.append(adj) 272 | return result 273 | 274 | def DistSquared(self,idx1,idx2): 275 | c1,r1 = self.CalculateCellColRow(idx1) 276 | c2,r2 = self.CalculateCellColRow(idx2) 277 | return (r1-r2)*(r1-r2) + (c1-c2)*(c1-c2) 278 | 279 | 280 | # Given a specific index and a list of all the other 281 | # indices around it, find the one closest. 282 | def FindNearestIndex(self,layerIndex,indices): 283 | distSq = [(self.DistSquared(layerIndex,idx),idx) for idx in indices] 284 | distSq.sort() 285 | return distSq[0][1] 286 | 287 | def BeginsWithNavPrefix(self, text): 288 | if text[:len(self.navPrefix)] == self.navPrefix: 289 | return True 290 | return False 291 | 292 | def Banner(self, text): 293 | print 294 | print "---------------------------------" 295 | print text 296 | print "---------------------------------" 297 | 298 | def IndicesToCells(self, indices): 299 | result = [self.CalculateCellColRow(index) for index in indices] 300 | return result 301 | 302 | def CellsToIndices(self, cells): 303 | result = [self.CalculateCellIndex(col, row) for col, row in cells] 304 | return result 305 | 306 | def ExtractRoomsData(self): 307 | if self.verbose: 308 | self.Banner("Extracting Rooms") 309 | 310 | KEY_ROOMS = MapDataExtractor.KEY_ROOMS 311 | KEY_WALKABLE = MapDataExtractor.KEY_WALKABLE 312 | KEY_PROPERTY_ROOM = self.FormatNavName(MapDataExtractor.KEY_PROPERTY_ROOM) 313 | 314 | # Create an output set to hold the data. 315 | self.outputDict[KEY_ROOMS] = {} 316 | 317 | # Create a dictionary for the rooms based on the tiles that 318 | # have the property. 319 | roomDict = {} 320 | tileDict = {} 321 | indexDict = {} 322 | for tileID in self.tileMap: 323 | if self.tileMap[tileID].has_key(KEY_PROPERTY_ROOM): 324 | roomID = int(self.tileMap[tileID][KEY_PROPERTY_ROOM]) 325 | tileDict[tileID] = roomID 326 | roomDict[roomID] = [] 327 | # Find the rooms layer. 328 | navRooms = self.FormatNavName(KEY_ROOMS) 329 | if navRooms not in self.layerMap: 330 | return self.FatalError("Layer %s not found in layers."%navRooms) 331 | layer = self.layerMap[navRooms] 332 | for layerIndex in layer: 333 | tileID = layer[layerIndex] 334 | if(tileID in tileDict): 335 | # This means the tile in the layer is a room tile marker. 336 | tileType = tileDict[tileID] 337 | roomDict[tileType].append(layerIndex) 338 | indexDict[layerIndex] = tileType 339 | self.outputDict[KEY_ROOMS] = roomDict 340 | self.outputDict[KEY_WALKABLE] = indexDict 341 | if self.verbose: 342 | roomKeys = roomDict.keys() 343 | roomKeys.sort() 344 | for key in roomKeys: 345 | print "Room: %d"%key 346 | print self.IndicesToCells(roomDict[key]) 347 | print 348 | return True 349 | 350 | def ExtractObjectsData(self): 351 | if self.verbose: 352 | self.Banner("Extracting Objects") 353 | # Figure out which layers to process. 354 | # Start with all the layer names. 355 | lNames = self.layerMap.keys() 356 | # Now start filtering out. 357 | lNames = [name for name in lNames if name != self.wallLayer] 358 | lNames = [name for name in lNames if name != self.floorLayer] 359 | lNames = [name for name in lNames if name not in self.excludeLayers] 360 | lNames = [name for name in lNames if not self.BeginsWithNavPrefix(name)] 361 | lNames.sort() 362 | # Go through each layer, creating an object set for each. 363 | # Store them in a dictionary by object name, with the data as a list of lists, 364 | # each sublist containing the indices for the object. 365 | subjectID = 1 366 | subjects = { } 367 | for objType in lNames: 368 | layer = self.layerMap[objType] 369 | keys = layer.keys() 370 | keys.sort() 371 | while len(keys) > 0: 372 | # Pop the first key. 373 | first = keys.pop(0) 374 | found = self.FindConnectedIndices(first,keys) 375 | # Remove the found from the keys 376 | keys = [key for key in keys if key not in found] 377 | subjects[subjectID] = [objType, found, []] 378 | subjectID += 1 379 | # Now that all the objects have been identified, do a cross 380 | # check of the objects with the activators and create a 381 | # subjectID link between an "Activator" and the object 382 | # closest to it that is NOT an activator. 383 | 384 | # Build up a master list of all the object indices EXCEPT 385 | # the ones used for the activators. 386 | subjectIndexMap = {} 387 | for subject in subjects: 388 | objType,indices,binding = subjects[subject] 389 | if objType in self.bindingLayers: 390 | continue 391 | for idx in indices: 392 | subjectIndexMap[idx] = subject 393 | objIndices = subjectIndexMap.keys() 394 | objIndices.sort() 395 | # Now go through the activators and search for the 396 | # nearest object. 397 | keys = subjects.keys() 398 | keys.sort() 399 | for act in self.bindingLayers: 400 | for subject in keys: 401 | objType, indices, binding = subjects[subject] 402 | if objType == act: 403 | # Find the nearest index 404 | nearest = self.FindNearestIndex(indices[0],objIndices) 405 | # Bind the two together 406 | bindingTo = subjectIndexMap[nearest] 407 | subjects[subject][2].append(bindingTo) 408 | subjects[bindingTo][2].append(subject) 409 | # Store off for later processing 410 | self.outputDict[MapDataExtractor.KEY_OBJECTS] = subjects 411 | 412 | if self.verbose: 413 | keys = subjects.keys() 414 | keys.sort() 415 | for key in keys: 416 | objType, indices, binding = subjects[key] 417 | print "-- [SubjectID: %d] [Type: %s] [BoundID: %s] %s"%(key,objType, binding,self.IndicesToCells(indices)) 418 | print 419 | return True 420 | 421 | def ExtractAdjacencyData(self): 422 | if self.verbose: 423 | self.Banner("Extracting Adjacency Information") 424 | # First, we need to build up a map of all the indexes and the 425 | # rooms that they are attached to. 426 | # We do this by inverting the rooms data. 427 | roomDict = self.outputDict[MapDataExtractor.KEY_ROOMS] 428 | indexDict = {} 429 | for room in roomDict: 430 | for layerIndex in roomDict[room]: 431 | if indexDict.has_key(layerIndex): 432 | return self.FatalError("Layer Index %s present twice in room dict."%layerIndex) 433 | # We are going to build up information for each index and put it into 434 | # this dictionary. The first element will be the room the index is in. 435 | indexDict[layerIndex] = [room] 436 | # Now we also need to remove any indexes for objects that are marked as "blocking". 437 | if len(self.blockingLayers) > 0: 438 | if self.verbose: 439 | print 440 | print "Removing Blocked Layers" 441 | for lname in self.blockingLayers: 442 | for idx in self.layerMap[lname]: 443 | if idx in indexDict: 444 | if self.verbose: 445 | col,row = self.CalculateCellColRow(idx) 446 | print "Removing (%4d, %4d) from consideration [Blocking %s]"%(col,row,lname) 447 | del indexDict[idx] 448 | 449 | # Build up the "doorDict". We'll need it later 450 | doorDict = { key:0 for key in self.layerMap[self.doorLayer].keys() if key != 0 } 451 | 452 | # For every layer index, look and figure out if there are adjacent cells for it. 453 | # Build this up as a walkable list for now. 454 | # In subsequent steps, we can figure out if the edge is 455 | # more complex. 456 | for layerIndex in indexDict: 457 | adjacentList = self.GetAdjacentIndexes(layerIndex) 458 | # Only keep the ones that are also in a room 459 | adjacentList = [adj for adj in adjacentList if adj in indexDict] 460 | # Now that we have a list of edges to adjacent cells, we have to 461 | # figure out what kind of edges they are. 462 | temp = [] 463 | for adj in adjacentList: 464 | # We know it must at least be "walkable" 465 | edgeType = MapDataExtractor.KEY_EDGE_WALK 466 | src = layerIndex 467 | des = adj 468 | srcRoom = indexDict[src][0] 469 | desRoom = indexDict[des][0] 470 | if srcRoom != desRoom: 471 | # Must be more complex 472 | edgeType = MapDataExtractor.KEY_EDGE_ROOM 473 | # If there is a door on both layer indices, then this must 474 | # be a door edge. 475 | if src in doorDict and des in doorDict: 476 | edgeType = MapDataExtractor.KEY_EDGE_DOOR 477 | temp.append((adj,edgeType)) 478 | indexDict[layerIndex].append(temp) 479 | self.outputDict[MapDataExtractor.KEY_ADJACENCY] = indexDict 480 | if self.verbose: 481 | keys = indexDict.keys() 482 | keys.sort() 483 | # To make this a little cleaner, we are going to construct to lists 484 | # of outputs. One for "walking" and one for "the rest". 485 | walkEdges = [] 486 | restEdges = [] 487 | for key in keys: 488 | colSrc, rowSrc = self.CalculateCellColRow(key) 489 | roomSrc = indexDict[key][0] 490 | for adj,etype in indexDict[key][1]: 491 | colDes, rowDes = self.CalculateCellColRow(adj) 492 | roomDes = indexDict[adj][0] 493 | if roomDes != roomSrc: 494 | restEdges.append("(%4d, %4d) --> (%4d, %4d) [Room %d -> %d] by %s Edge." % ( 495 | colSrc, rowSrc, colDes, rowDes, roomSrc, roomDes, etype)) 496 | else: 497 | walkEdges.append("(%4d, %4d) --> (%4d, %4d) [Room %d]" % ( 498 | colSrc, rowSrc, colDes, rowDes, roomDes)) 499 | print 500 | print "Walking Edges:" 501 | if self.reallyVerbose: 502 | for item in walkEdges: 503 | print item 504 | else: 505 | print "" 506 | print 507 | print "Other Types of Edges" 508 | for item in restEdges: 509 | print item 510 | 511 | return True 512 | 513 | def ExportTilemapData(self,writer): 514 | # Export the Tilemap Data 515 | writer.writerow(["Tilemap", "File", self.tiledFile]) 516 | writer.writerow(["Tilemap", "TileHeight", self.tileHeight]) 517 | writer.writerow(["Tilemap", "TileWidth", self.tileWidth]) 518 | writer.writerow(["Tilemap", "LayerHeight", self.layerHeight]) 519 | writer.writerow(["Tilemap", "LayerWidth", self.layerWidth]) 520 | writer.writerow(["Tilemap", "BlockingLayers"] + self.blockingLayers) 521 | writer.writerow(["Tilemap", "BindingLayers"] + self.bindingLayers) 522 | 523 | def ExportGraphData(self,writer): 524 | # Export the basic graph data 525 | # Indices 526 | indices = self.outputDict[MapDataExtractor.KEY_WALKABLE].keys() 527 | indices.sort() 528 | # Split it into chunks 529 | chunkCount = 0 530 | chunks = [indices[x:x + MapDataExtractor.CSV_EXPORT_COLUMNS] 531 | for x in xrange(0, len(indices), MapDataExtractor.CSV_EXPORT_COLUMNS)] 532 | for chunk in chunks: 533 | chunkCount += len(chunk) 534 | writer.writerow(["Graph", "Indices"] + chunk) 535 | if chunkCount != len(indices): 536 | return self.FatalError("Chunk count = %d, but index count = %d!!!" % (chunkCount, len(indices))) 537 | # Adjacency 538 | adj = self.outputDict[MapDataExtractor.KEY_ADJACENCY] 539 | adjIdx = adj.keys() 540 | adjIdx.sort() 541 | for srcIdx in adjIdx: 542 | srcRoom = adj[srcIdx][0] 543 | for desIdx, edgeType in adj[srcIdx][1]: 544 | desRoom = adj[desIdx][0] 545 | writer.writerow(["Graph", "Adjacent", srcIdx, desIdx, srcRoom, desRoom, edgeType]) 546 | 547 | def ExportRoomData(self,writer): 548 | # Export the Room Indices 549 | roomDict = self.outputDict[MapDataExtractor.KEY_ROOMS] 550 | rooms = roomDict.keys() 551 | rooms.sort() 552 | for room in rooms: 553 | indices = roomDict[room][:] 554 | indices.sort() 555 | chunkCount = 0 556 | chunks = [indices[x:x + MapDataExtractor.CSV_EXPORT_COLUMNS] 557 | for x in xrange(0, len(indices), MapDataExtractor.CSV_EXPORT_COLUMNS)] 558 | for chunk in chunks: 559 | chunkCount += len(chunk) 560 | writer.writerow(["Room", "Indices", room] + chunk) 561 | if chunkCount != len(indices): 562 | return self.FatalError("Chunk count = %d, but index count = %d!!!" % (chunkCount, len(indices))) 563 | def ExportObjectData(self,writer): 564 | subjects = self.outputDict[MapDataExtractor.KEY_OBJECTS] 565 | keys = subjects.keys() 566 | keys.sort() 567 | for key in keys: 568 | objType, indices, binding = subjects[key] 569 | writer.writerow(["Object", "Define", key, objType]) 570 | writer.writerow(["Object", "Indices", key] + indices) 571 | writer.writerow(["Object", "Binding", key] + binding) 572 | 573 | 574 | def ExportData(self): 575 | with open(self.outFile, 'wb') as csvfile: 576 | writer = csv.writer(csvfile) 577 | writer.writerow(["PKey","SKey","Parameters"]) 578 | # Basic Tilemap parameters 579 | self.ExportTilemapData(writer) 580 | # Nodes and Adjacency 581 | self.ExportGraphData(writer) 582 | # Rooms 583 | self.ExportRoomData(writer) 584 | # Objects 585 | self.ExportObjectData(writer) 586 | 587 | return True 588 | return False 589 | 590 | def CheckInputs(self): 591 | if self.doorLayer not in self.layerMap: 592 | return self.FatalError("Door Layer %s not in layers." % self.doorLayer) 593 | if self.wallLayer not in self.layerMap: 594 | return self.FatalError("Wall Layer %s not in layers." % self.wallLayer) 595 | if self.floorLayer not in self.layerMap: 596 | return self.FatalError("Floor Layer %s not in layers." % self.floorLayer) 597 | for exclude in self.excludeLayers: 598 | if exclude not in self.layerMap: 599 | return self.FatalError("Exclude Layer %s not in layers."%exclude) 600 | for blocking in self.blockingLayers: 601 | if blocking not in self.layerMap: 602 | return self.FatalError("Blocking Layer %s not in layers."%blocking) 603 | return True 604 | 605 | def ProcessMap(self, 606 | tiledFile, 607 | floorLayer, 608 | wallLayer, 609 | doorLayer, 610 | navPrefix, 611 | outFile, 612 | excludeLayers, 613 | blockingLayers, 614 | bindingLayers, 615 | diagonalEdges, 616 | verbose, 617 | reallyVerbose): 618 | # Cache inputs 619 | self.tiledFile = tiledFile 620 | self.floorLayer = floorLayer 621 | self.doorLayer = doorLayer 622 | self.wallLayer = wallLayer 623 | self.navPrefix = navPrefix 624 | self.outFile = outFile 625 | self.excludeLayers = excludeLayers 626 | self.blockingLayers = blockingLayers 627 | self.bindingLayers = bindingLayers 628 | self.verbose = verbose 629 | self.diagonalEdges = diagonalEdges 630 | self.reallyVerbose = reallyVerbose 631 | 632 | # Pull in the tile map. 633 | if not self.LoadTiledMap(tiledFile): 634 | return False 635 | 636 | if not self.CheckInputs(): 637 | return False 638 | 639 | # Extract the rooms. 640 | if not self.ExtractRoomsData(): 641 | return False 642 | 643 | # Extract the objects. 644 | if not self.ExtractObjectsData(): 645 | return False 646 | 647 | # Extract adjacency 648 | if not self.ExtractAdjacencyData(): 649 | return False 650 | 651 | if not self.ExportData(): 652 | return False 653 | 654 | return True 655 | 656 | if __name__ == "__main__": 657 | arguments = docopt.docopt(__doc__) 658 | # When testing is done, this is where 659 | # test arguments are inserted. 660 | 661 | # Some temporary Arguments 662 | """ 663 | arguments[""] = "tiled.tmx" 664 | arguments["--verbose"] = True 665 | arguments["--binding"] = ["Activators"] 666 | """ 667 | 668 | # It is a good idea to print these out so you 669 | # can see if you typed in something incorrectly. 670 | print "-----------------------------------" 671 | print "Inputs:" 672 | args = arguments.keys() 673 | args.sort() 674 | for arg in args: 675 | print "%-25s %s" % (arg, arguments[arg]) 676 | print "-----------------------------------" 677 | 678 | 679 | # Parse the arguments 680 | tiledFile = arguments[""] 681 | excludeLayers = arguments["--exclude"] 682 | blockingLayers = arguments["--blocking"] 683 | bindingLayers = arguments["--binding"] 684 | doorLayer = arguments["--doorLayer"] 685 | wallLayer = arguments["--wallLayer"] 686 | floorLayer = arguments["--floorLayer"] 687 | navPrefix = arguments["--navPrefix"] 688 | outFile = arguments["--outFile"] 689 | reallyVerbose = arguments['--reallyVerbose'] 690 | verbose = arguments["--verbose"] or reallyVerbose 691 | diagonalEdges = not arguments["--noDiagonalEdges"] 692 | 693 | extractor = MapDataExtractor() 694 | extractor.ProcessMap(tiledFile, 695 | floorLayer, 696 | wallLayer, 697 | doorLayer, 698 | navPrefix, 699 | outFile, 700 | excludeLayers, 701 | blockingLayers, 702 | bindingLayers, 703 | diagonalEdges, 704 | verbose, 705 | reallyVerbose 706 | ) 707 | -------------------------------------------------------------------------------- /MapTiler.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------- 2 | # License 3 | # ------------------------------------------------------------- 4 | # Permission is hereby granted, free of charge, to any person 5 | # obtaining a copy of this software and associated 6 | # documentation files (the "Software"), to deal in the Software 7 | # without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to 10 | # whom the Software is furnished to do so, subject to the 11 | # following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall 14 | # be included in all copies or substantial portions of the 15 | # Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 18 | # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 19 | # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 20 | # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | # ------------------------------------------------------------- 26 | 27 | 28 | """ 29 | Convert a series of same-sized images into a tile set and tile map that can be 30 | opened in Tiled. This is designed specifically for taking a map constructed 31 | as multiple layers and creating a Tiled file automatically. 32 | 33 | Usage: MapTiler.py [--tileWidth=WIDTH] 34 | [--tileHeight=HEIGHT] 35 | [--tileOffX=OFFX] 36 | [--tileOffY=OFFY] 37 | [--tileInsetX=INSETX] 38 | [--tileInsetY=INSETY] 39 | [--filePattern=PATTERN] 40 | [--floorLayer=FLOORLAYER] 41 | [--wallLayer=WALLLAYER] 42 | [--portalLayer=PORTALLAYER] 43 | [--doorLayer=DOORLAYER] 44 | [--forceSquareTileset] 45 | [--overwriteExisting] 46 | [--mergeExisting] 47 | [--outTileset=OUTTILESET] 48 | [--outTiled=OUTTILED] 49 | [--outNavPrefix=OUTNAVPRE] 50 | [--verbose] 51 | [...] 52 | 53 | Arguments: 54 | 55 | 56 | Options: 57 | layerImage A list of image files to be used as 58 | inputs. They will be processed and 59 | placed into the .tmx file in the 60 | order entered. Or sorted if a 61 | search pattern is used. 62 | --tileWidth=WIDTH The tile width in pixels. 63 | [Default: 64] 64 | --tileHeight=HEIGHT The tile height in pixels. 65 | [Default: 64] 66 | --tileOffX=OFFX Offset pixels in the image in the 67 | horizontal direction. May NOT 68 | be negative. 69 | [Default: 0] 70 | --tileOffY=OFFY Offset pixels in the image in the 71 | vertical direction. May NOT 72 | be negative. 73 | [Default: 0] 74 | --tileInsetX=INSETX Offset pixels from the right edge. 75 | [Default: 0] 76 | --tileInsetY=INSETY Offset pixels from the bottom edge. 77 | [Default: 0] 78 | --filePattern=PATTERN A glob pattern for image files. 79 | --outTileset=OUTTILESET The output tileset image file. 80 | [Default: tileset.png] 81 | --outTiled=OUTTILED The output Tiled file in .tmx format. 82 | [Default: tiled.tmx] 83 | --forceSquareTileset If present, the tileset image will be 84 | a square image. The output tileset image 85 | is always a power of 2 in size. 86 | --floorLayer=FLOORLAYER Define the name for the floor layer to be 87 | used in nav map generation. See below. 88 | --wallLayer=WALLLAYER Define the name for the wall layer to be 89 | used in nav map generation. See below. 90 | --portalLayer=PORTALLAYER Define the name for the portal layer to be 91 | used in nav map generation. See below. 92 | --doorLayer=DOORLAYER Define the name for the door layer to be 93 | used in nav map generation. 94 | --outNavPrefix=OUTNAVPRE If the floorLayer, wallLayer, and portalLayer options are 95 | specified, navigation data will be generated. This 96 | option specifies the prefix for the navigation data, 97 | including layers and properties. 98 | [Default: NAV_] 99 | --verbose If present, give output while working. 100 | --overwriteExisting Overwrite output files with new files. 101 | 102 | --mergeExisting Overwrite the tileset file and attempt 103 | to merges the layers in the new file 104 | with the existing file. See below. 105 | 106 | 107 | Merging and Overwriting 108 | ------------------------------------------------------------------------------ 109 | The script will check if the output files exist first. The default 110 | behavior IS TO FAIL if they do. This is to prevent you from accidentally 111 | overwriting your data files. 112 | 113 | If the --overwriteExisting flag is set, the existing files will be overwritten 114 | without checking. If the --mergeExisting option is set, the script will 115 | overwrite the tile set and attempt to merge the tiled file. Ther merge will 116 | fail if the width/height in tiles of the layers has changed. 117 | 118 | When failing, the original files will be preserved. 119 | 120 | Merge Operations 121 | ---------------------------------------------- 122 | (1) The merge operation will merge in processed layers with existing 123 | layers already in the file. 124 | It does this by iterating through the existing 125 | tag of the layer (if it can find it) and updating each 126 | element with the gid of the element from the new set. Otherwise, 127 | it does not touch ANY DATA. If it cannot find the layer in the 128 | existing file, it is added. 129 | 130 | If you have other tilesets in the 131 | existing file with a gid = "1", this operation will generate 132 | bad results. 133 | 134 | (2) If you created a room map (floorLayer, wallLayer, doorLayer, portalLayer), 135 | then the existing tileset will be scanned and all properties for the outNavPrefix + 136 | "ROOM" will be erased. 137 | 138 | The new tiles for rooms will have the property added with 139 | the room number as the value. 140 | 141 | 142 | The --overwriteExisting and --mergeExisting are mutually exclusive. 143 | 144 | 145 | Website: http://www.NonlinearIdeas.com 146 | Repository: https://github.com/NonlinearIdeas/Map-Tiler 147 | Report Issues: https://github.com/NonlinearIdeas/Map-Tiler 148 | License: MIT License - See specific text in source code. 149 | Copyright: Copyright (c) 2015 Nonlinear Ideas Inc (contact@nlideas.com) 150 | """ 151 | 152 | import glob 153 | import os 154 | import Image 155 | import ImageChops 156 | import ImageDraw 157 | import ImageFont 158 | from lxml import etree 159 | import docopt 160 | import math 161 | import datetime 162 | import random 163 | 164 | 165 | class MapTiler(object): 166 | # Transformations are numbered 0-7. The transformations 167 | # each have three flags, indicating a flip across the X axis, 168 | # a flip across the y axis, or a diagonal flip. These correspond 169 | # to three bits that are stored in Tiled GIDs for each tile 170 | # when it is put into the layer. 171 | # 172 | # These transformations are also equivalent to a flip along the 173 | # X axis (MirrorX) followed by a rotation of 0 x 90 degrees. 174 | # 175 | # There are two different "transform" approaches because the 176 | # PIL's transform(...) function does not perform a "Flip Diagonal", 177 | # while Tiled's bits do. The former is used to determine which 178 | # transformation is necessary, while the second is used to 179 | # store it in Tiled. 180 | TRANSFORM_LIST = [ 181 | # xForm, mirrorX, rot90 182 | (0, False, 0), 183 | (1, False, 1), 184 | (2, False, 2), 185 | (3, False, 3), 186 | (4, True, 0), 187 | (5, True, 1), 188 | (6, True, 2), 189 | (7, True, 3), 190 | ] 191 | 192 | TRANSFORM_DICT = { 193 | # xForm, flipX, flipY, flipD 194 | 0: ( False, False, False), 195 | 1: ( False, True, True), 196 | 2: ( True, True, False), 197 | 3: ( True, False, True), 198 | 4: ( True, False, False), 199 | 5: ( False, False, True), 200 | 6: ( False, True, False), 201 | 7: ( True, True, True), 202 | } 203 | 204 | FLIPPED_HORIZONTALLY_FLAG = 0x80000000 205 | FLIPPED_VERTICALLY_FLAG = 0x40000000 206 | FLIPPED_DIAGONALLY_FLAG = 0x20000000 207 | 208 | PROPERTY_ROOM = "ROOM" 209 | 210 | DRAW_FONT = "Transformers Movie.ttf" 211 | 212 | def __init__(self): 213 | self.Reset() 214 | 215 | def Reset(self): 216 | self.tileProperties = {} 217 | 218 | # Updates the gid for a tile based on the rotation 219 | # and flipX flag passed in from the PyxelEdit element. 220 | def UpdateGIDForRotation(self, gid, xForm): 221 | # Constants used in Tiled to indicate flip/rotation 222 | flipX, flipY, flipD = MapTiler.TRANSFORM_DICT[xForm] 223 | if flipX: 224 | gid += MapTiler.FLIPPED_HORIZONTALLY_FLAG 225 | if flipY: 226 | gid += MapTiler.FLIPPED_VERTICALLY_FLAG 227 | if flipD: 228 | gid += MapTiler.FLIPPED_DIAGONALLY_FLAG 229 | return gid 230 | 231 | def ExtractTileIndex(self,gid): 232 | return gid & \ 233 | MapTiler.FLIPPED_DIAGONALLY_FLAG & \ 234 | MapTiler.FLIPPED_HORIZONTALLY_FLAG & \ 235 | MapTiler.FLIPPED_DIAGONALLY_FLAG 236 | 237 | 238 | def CreateLayerFiles(self, inputFilePattern = None, fileList = []): 239 | if inputFilePattern: 240 | files = glob.glob(inputFilePattern) 241 | if len(files) == 0: 242 | print "No files found matching pattern %s." % self.inputFilePattern 243 | return False 244 | else: 245 | files.sort() 246 | fileList += files 247 | # If there are no files to process, we are done. 248 | if len(fileList) == 0: 249 | print "No files to process." 250 | return False 251 | self.layerFiles = [] 252 | self.layerNames = [] 253 | for file in fileList: 254 | if not os.path.exists(file): 255 | print "File %s does not exist."%file 256 | print "Unable to coninue." 257 | return False 258 | if file != self.outTilesetFile: 259 | self.layerFiles.append(file) 260 | self.layerNames.append(os.path.splitext(os.path.split(file)[1])[0]) 261 | return True 262 | 263 | def CheckImageSizes(self): 264 | # Open the first file and get its size. 265 | imgFile = self.LoadCroppedImage(self.layerFiles[0]) 266 | imageWidth, imageHeight = imgFile.size 267 | if imageWidth % self.tileWidth != 0: 268 | print "File %s's Width %d cannot be divided evenly by tile width %d." % ( 269 | self.layerFiles[0], imageWidth, self.tileWidth) 270 | return False 271 | if imageHeight % self.tileHeight != 0: 272 | print "File %s's Height %d cannot be divided evenly by tile height %d." % ( 273 | self.layerFiles[0], imageHeight, self.tileHeight) 274 | return False 275 | self.imageWidth = imageWidth 276 | self.imageHeight = imageHeight 277 | self.layerWidth = imageWidth / self.tileWidth 278 | self.layerHeight = imageHeight / self.tileHeight 279 | self.layerTiles = self.layerWidth * self.layerHeight 280 | for other in self.layerFiles[1:]: 281 | imgFile = self.LoadCroppedImage(other) 282 | width, height = imgFile.size 283 | if width != self.imageWidth or height != self.imageHeight: 284 | print "Image %s Size (%d x %d) does not match base size (%d x %d)" % ( 285 | other, width, height, self.imageWidth, self.imageHeight) 286 | return False 287 | return True 288 | 289 | def CalculateImageIndexFromCell(self, col, row): 290 | index = row * self.layerWidth + col 291 | return index 292 | 293 | def CalculateImageRowCell(self, index): 294 | row = index / self.layerWidth 295 | col = index % self.layerWidth 296 | return col, row 297 | 298 | def CalculateSubimageRect(self, index): 299 | row = index / self.layerWidth 300 | col = index % self.layerWidth 301 | x0 = col * self.tileWidth 302 | x1 = x0 + self.tileWidth 303 | y0 = row * self.tileHeight 304 | y1 = y0 + self.tileHeight 305 | return (x0, y0, x1, y1) 306 | 307 | def ExtractSubimage(self, im, index): 308 | rect = self.CalculateSubimageRect(index) 309 | ex = im.crop(rect) 310 | return ex 311 | 312 | def GetOccupiedTiles(self,layerName): 313 | result = [] 314 | layerDict = self.layerDict[layerName] 315 | for idx in layerDict: 316 | tileIdx, xForm = layerDict[idx] 317 | if tileIdx > 0: 318 | result.append(idx) 319 | return result 320 | 321 | def DrawCenteredText(self,image,text,size=16,color=(0,0,0)): 322 | draw = ImageDraw.Draw(image) 323 | font = ImageFont.truetype(MapTiler.DRAW_FONT,size) 324 | szX,szY = image.size 325 | drX,drY = font.getsize(text) 326 | xPos = (szX - drX)/2 327 | yPos = (szY - drY)/2 328 | draw.text((xPos,yPos),text,color,font) 329 | 330 | def CreateRandomColorTile(self,channelMin=64,channelMax=200,opacity=64,text=None): 331 | r = random.randint(channelMin,channelMax) 332 | g = random.randint(channelMin,channelMax) 333 | b = random.randint(channelMin,channelMax) 334 | tile = Image.new("RGBA", (self.tileWidth, self.tileHeight), (r,g,b,opacity)) 335 | if text: 336 | self.DrawCenteredText(tile,text,self.tileWidth/3,(0,0,0)) 337 | return tile 338 | 339 | def ExportNavFiles(self): 340 | pass 341 | 342 | def CreateBlockingData(self): 343 | self.blockedList = self.GetOccupiedTiles(self.navWallLayer) 344 | 345 | def CreateWalkableData(self): 346 | floorTiles = self.GetOccupiedTiles(self.navFloorLayer) 347 | wallTiles = self.GetOccupiedTiles(self.navWallLayer) 348 | self.walkableList = [idx for idx in floorTiles if idx not in wallTiles] 349 | 350 | def CreateRoomData(self): 351 | floorTiles = self.GetOccupiedTiles(self.navFloorLayer) 352 | wallTiles = self.GetOccupiedTiles(self.navWallLayer) 353 | portalTiles = self.GetOccupiedTiles(self.navPortalLayer) 354 | doorTiles = self.GetOccupiedTiles(self.navDoorLayer) 355 | for idx in doorTiles: 356 | if idx not in portalTiles: 357 | portalTiles.append(idx) 358 | walkable = [idx for idx in floorTiles if idx not in wallTiles] 359 | portals = [idx for idx in portalTiles if idx not in wallTiles] 360 | rooms = [] 361 | # Use a flood fill algorithm to figure out where the rooms are. 362 | while len(walkable) > 0: 363 | # Start a list for the tiles in this room. 364 | roomTiles = [] 365 | # Push the first element in the walkables onto the list. 366 | queue = [walkable[0]] 367 | while len(queue) > 0: 368 | # Pull it off the stack 369 | idx = queue.pop(0) 370 | # It is in the room. 371 | roomTiles.append(idx) 372 | x, y = self.CalculateImageRowCell(idx) 373 | adjacent = [self.CalculateImageIndexFromCell(x, y + 1), 374 | self.CalculateImageIndexFromCell(x, y - 1), 375 | self.CalculateImageIndexFromCell(x + 1, y), 376 | self.CalculateImageIndexFromCell(x - 1, y)] 377 | for adj in adjacent: 378 | if adj in roomTiles: 379 | continue 380 | if adj not in walkable: 381 | # Already considered or not walkable 382 | continue 383 | if adj in queue: 384 | # Already in consideration 385 | continue 386 | if idx in portals and adj in portals: 387 | # Don't expand beyond doors. 388 | continue 389 | queue.append(adj) 390 | # Remove it from walkable...we've looked at it now. 391 | walkable.remove(idx) 392 | rooms.append(roomTiles) 393 | # Cache off the rooms 394 | self.roomDict = {} 395 | for idx in xrange(len(rooms)): 396 | self.roomDict[idx] = rooms[idx] 397 | 398 | def CreateRoomsLayer(self): 399 | roomDict = self.roomDict 400 | keys = roomDict.keys() 401 | keys.sort() 402 | # Create the layer 403 | layerDict = { idx:(0,0) for idx in xrange(self.layerTiles) } 404 | for idx in keys: 405 | tile = self.CreateRandomColorTile(opacity=128,text="R%d"%idx) 406 | tileIdx = len(self.imageDict) 407 | self.imageDict[tileIdx] = tile 408 | for roomTile in roomDict[idx]: 409 | layerDict[roomTile] = (tileIdx,0) 410 | self.tileProperties[tileIdx] = [(self.outNavPrefix + MapTiler.PROPERTY_ROOM,"%s"%idx)] 411 | lname = self.outNavPrefix + "Rooms" 412 | self.layerDict[lname] = layerDict 413 | self.layerNames.append(lname) 414 | 415 | def CreateWalkableLayer(self): 416 | walkable = self.walkableList 417 | # Add a tile to represent a blocked cell 418 | walkTile = Image.new("RGBA", (self.tileWidth, self.tileHeight), (0, 0, 128, 64)) 419 | walkTileIdx = len(self.imageDict) 420 | self.imageDict[walkTileIdx] = walkTile 421 | # Add a layer to represent the blocked layer 422 | lname = self.outNavPrefix + "Walkable" 423 | layerDict = {} 424 | for idx in xrange(self.layerTiles): 425 | if idx in walkable: 426 | layerDict[idx] = (walkTileIdx, 0) 427 | else: 428 | layerDict[idx] = (0, 0) 429 | self.layerDict[lname] = layerDict 430 | self.layerNames.append(lname) 431 | 432 | def CreateBlockingLayer(self): 433 | blocked = self.blockedList 434 | # Add a tile to represent a blocked cell 435 | blockedTile = Image.new("RGBA",(self.tileWidth,self.tileHeight), (128,0,0,64)) 436 | blockedTileIdx = len(self.imageDict) 437 | self.imageDict[blockedTileIdx] = blockedTile 438 | # Add a layer to represent the blocked layer 439 | lname = self.outNavPrefix + "Blocked" 440 | layerDict = { idx: (0,0) for idx in xrange(self.layerTiles)} 441 | for idx in xrange(self.layerTiles): 442 | if idx in blocked: 443 | layerDict[idx] = (blockedTileIdx,0) 444 | self.layerDict[lname] = layerDict 445 | self.layerNames.append(lname) 446 | 447 | def CreateNavData(self): 448 | if self.createNavData: 449 | self.CreateWalkableData() 450 | self.CreateBlockingData() 451 | self.CreateRoomData() 452 | 453 | self.CreateWalkableLayer() 454 | self.CreateBlockingLayer() 455 | self.CreateRoomsLayer() 456 | return True 457 | 458 | def CreateTileset(self): 459 | self.imageDict = {} 460 | self.layerDict = {} 461 | self.tilesCreated = 0 462 | self.tilesPossible = 0 463 | # Create an empty tile. 464 | # There is almost ALWAYS at least one of these. 465 | emptyTile = Image.new('RGBA', (self.tileWidth, self.tileHeight ), (0, 0, 0, 0)) 466 | self.imageDict[0] = emptyTile 467 | self.tilesCreated += 1 468 | subimgIdx = 1 469 | tilesToProcess = len(self.layerFiles)*self.layerTiles 470 | tilesProcessed = 0 471 | lastTileMatchIndex = 0 472 | for fname in self.layerFiles: 473 | lname = os.path.split(fname)[1] 474 | lname = os.path.splitext(lname)[0] 475 | if self.verbose: 476 | print "Creating Subimages for layer %s" % lname 477 | self.layerDict[lname] = {} 478 | img = self.LoadCroppedImage(fname) 479 | foundXform = False 480 | for idx in xrange(self.layerTiles): 481 | subimg = self.ExtractSubimage(img, idx) 482 | foundXform = False 483 | col, row = self.CalculateImageRowCell(idx) 484 | self.tilesPossible += 1 485 | tilesProcessed += 1 486 | # A slight optimization here. Tiles are being scanned 487 | # horizontally and they often repeat. The last tile is 488 | # a good candidate for a match, so check this one first. 489 | # Some "Results" 490 | # Processing a small map (JanHouse.png) changed the process time from 8 seconds to 6 seconds. 491 | # Processing a large mpa (kmare.png) changed the process time from 14:51 to 8:48. 492 | # 493 | # This approach appears to have some merit. 494 | # 495 | xForm = self.FindImageTransformation(subimg, self.imageDict[lastTileMatchIndex]) 496 | if xForm != None: 497 | self.layerDict[lname][idx] = (lastTileMatchIndex, xForm) 498 | if self.verbose: 499 | print "[%5.1f%% Complete] [%5.1f%% Eff] Layer %s, Index %d (%d,%d) maps onto image %d, xForm %d." % ( 500 | 100 * tilesProcessed / tilesToProcess, 501 | 100 * (1.0 - self.tilesCreated * 1.0 / self.tilesPossible), 502 | lname, idx, row, col, lastTileMatchIndex, xForm ) 503 | continue 504 | 505 | for desIdx in self.imageDict.keys(): 506 | # Already checked this one. 507 | if desIdx == lastTileMatchIndex: 508 | continue 509 | xForm = self.FindImageTransformation(subimg, self.imageDict[desIdx]) 510 | if xForm != None: 511 | # We have an equivalent transformation 512 | self.layerDict[lname][idx] = (desIdx, xForm) 513 | if self.verbose: 514 | print "[%5.1f%% Complete] [%5.1f%% Eff] Layer %s, Index %d (%d,%d) maps onto image %d, xForm %d."%( 515 | 100*tilesProcessed/tilesToProcess, 516 | 100*(1.0-self.tilesCreated*1.0/self.tilesPossible), 517 | lname,idx,row,col,desIdx,xForm ) 518 | foundXform = True 519 | lastTileMatchIndex = desIdx 520 | break 521 | 522 | if not foundXform: 523 | # Keep this one. 524 | self.imageDict[subimgIdx] = subimg 525 | self.layerDict[lname][idx] = (subimgIdx, 0) 526 | if self.verbose: 527 | print "[%5.1f%% Complete] [%5.1f%% Eff] Layer %s, Index %d (%d,%d) is a new image."%( 528 | 100.0 * tilesProcessed / tilesToProcess, 529 | 100 * (1.0 - self.tilesCreated * 1.0 / self.tilesPossible), 530 | lname,idx,row,col) 531 | # Increment the subimage index for the next one. 532 | lastTileMatchIndex = subimgIdx 533 | self.tilesCreated += 1 534 | subimgIdx += 1 535 | return True 536 | 537 | def DumpTilemap(self): 538 | print "---------------------------------" 539 | print 'Tile Map' 540 | print "---------------------------------" 541 | for fname in self.layerFiles: 542 | lname = os.path.split(fname)[1] 543 | lname = os.path.splitext(lname)[0] 544 | print "Layer:", lname 545 | for idx in xrange(self.layerTiles): 546 | col, row = self.CalculateImageRowCell(idx) 547 | print " -[%d] (%d, %d) %s" % (idx, col, row, self.layerDict[lname][idx]) 548 | print 549 | 550 | # Determine if two images are the same by comparing rotations and 551 | # reflections between them. If a transformation can be found that 552 | # turns the first into the second, return it. Otherwise return None. 553 | # This is NOT a trivial operation. 554 | def FindImageTransformation(self, im1org, im2org): 555 | for xForm, mirrorX, rot90 in MapTiler.TRANSFORM_LIST: 556 | im2 = im2org 557 | if mirrorX: 558 | im2 = im2.transpose(Image.FLIP_LEFT_RIGHT) 559 | if rot90 == 1: 560 | im2 = im2.transpose(Image.ROTATE_90) 561 | elif rot90 == 2: 562 | im2 = im2.transpose(Image.ROTATE_180) 563 | elif rot90 == 3: 564 | im2 = im2.transpose(Image.ROTATE_270) 565 | if im1org.size != im2.size: 566 | # Don't compare images that are not the same size. 567 | continue 568 | if ImageChops.difference(im1org, im2).getbbox() is None: 569 | # They are the same now 570 | return xForm 571 | return None 572 | 573 | def FindNextPowerOfTwo(self, N): 574 | k = 1 575 | while k < N: 576 | k = k * 2 577 | return k 578 | 579 | def ExportTileset(self): 580 | # How many tiles do we have? 581 | tileCount = len(self.imageDict) 582 | # Need to create a "square" image that is a power of 2 583 | # This helps with GPUs, etc. 584 | imageDimWidth = self.FindNextPowerOfTwo(math.sqrt(tileCount)) 585 | imageDimHeight = imageDimWidth 586 | if self.forceSquareTileset: 587 | # To save on space, we'll decrement the dimension by 1 until we 588 | # can't fit the tiles. 589 | while imageDimHeight * imageDimWidth > tileCount: 590 | imageDimHeight = imageDimHeight / 2 591 | imageDimHeight = imageDimHeight * 2 592 | imageWidth = imageDimWidth * self.tileWidth 593 | imageHeight = imageDimHeight * self.tileHeight 594 | self.tilesetWidth = imageWidth 595 | self.tilesetHeight = imageHeight 596 | print "The tile size is (%d x %d) pixels wide x high."%(self.tileWidth,self.tileHeight) 597 | print "For %d tiles, the image size will be %d x %d tiles (%d x %d pixels)." % ( 598 | tileCount, imageDimWidth, imageDimHeight, imageWidth, imageHeight) 599 | print "Efficiency = 100%%(1-Tiles Created / Tiles Possible) => 100%%(1-%d/%d) = %4.1f%%."%( 600 | self.tilesCreated, 601 | self.tilesPossible, 602 | 100*(1.0-self.tilesCreated*1.0/self.tilesPossible)) 603 | # Create the output image 604 | imgOut = Image.new('RGBA', (imageWidth, imageHeight), (0, 0, 0, 0)) 605 | for idx in xrange(tileCount): 606 | row = idx / imageDimWidth 607 | col = idx % imageDimWidth 608 | x0 = col * self.tileWidth 609 | y0 = row * self.tileHeight 610 | x1 = x0 + self.tileWidth 611 | y1 = y0 + self.tileHeight 612 | imgOut.paste(self.imageDict[idx], (x0, y0, x1, y1)) 613 | print "Saving tileset to %s." % self.outTilesetFile 614 | imgOut.save(self.outTilesetFile) 615 | return True 616 | 617 | def LoadCroppedImage(self,fileName): 618 | image = Image.open(fileName) 619 | w, h = image.size 620 | x0 = self.tileOffX 621 | y0 = self.tileOffY 622 | x1 = w - self.tileInsetX 623 | y1 = h - self.tileInsetY 624 | img = image.crop((x0, y0, x1, y1)) 625 | if img.mode != "RGBA": 626 | img = img.convert("RGBA") 627 | return img 628 | 629 | def CreateTiledFile(self): 630 | # Build the root node ("map") 631 | outRoot = etree.Element("map") 632 | outRoot.attrib["version"] = "1.0" 633 | outRoot.attrib["orientation"] = "orthogonal" 634 | outRoot.attrib["renderorder"] = "left-up" 635 | outRoot.attrib["width"] = "%s" % self.layerWidth 636 | outRoot.attrib["height"] = "%s" % self.layerHeight 637 | outRoot.attrib["tilewidth"] = "%s" % self.tileWidth 638 | outRoot.attrib["tileheight"] = "%s" % self.tileHeight 639 | 640 | # Build the tileset 641 | tileset = etree.SubElement(outRoot, "tileset") 642 | tileset.attrib["firstgid"] = "1" 643 | tileset.attrib["name"] = os.path.splitext(os.path.split(self.outTilesetFile)[1])[0] 644 | tileset.attrib["tilewidth"] = "%s" % self.tileWidth 645 | tileset.attrib["tileheight"] = "%s" % self.tileHeight 646 | 647 | # Add the image information for the tileset 648 | imgElem = etree.SubElement(tileset, "image") 649 | imgElem.attrib["source"] = self.outTilesetFile 650 | imgElem.attrib["width"] = "%s" % self.tilesetWidth 651 | imgElem.attrib["height"] = "%s" % self.tilesetHeight 652 | 653 | # Add any special properties that we have put into the tiles. 654 | for tileID in self.tileProperties: 655 | tile = etree.SubElement(tileset,"tile") 656 | tile.attrib["id"] = "%s"%tileID 657 | properties = etree.SubElement(tile,"properties") 658 | for name,value in self.tileProperties[tileID]: 659 | property = etree.SubElement(properties,"property") 660 | property.attrib["name"] = name 661 | property.attrib["value"] = "%s"%value 662 | 663 | 664 | # Now iterate over the layers and pull out each one. 665 | for lname in self.layerNames: 666 | self.CreateXMLLayer(outRoot,lname) 667 | outTree = etree.ElementTree(outRoot) 668 | return outTree 669 | 670 | def CreateXMLLayer(self,outRoot,layerName): 671 | layer = etree.SubElement(outRoot, "layer") 672 | layer.attrib["name"] = layerName 673 | layer.attrib["width"] = "%s" % self.layerWidth 674 | layer.attrib["height"] = "%s" % self.layerHeight 675 | data = etree.SubElement(layer, "data") 676 | # In each layer, there are width x height tiles. 677 | for idx in xrange(self.layerTiles): 678 | tile = etree.SubElement(data, "tile") 679 | gid, xForm = self.layerDict[layerName][idx] 680 | if (gid == 0): 681 | # This is the "empty" tile 682 | tile.attrib["gid"] = "0" 683 | else: 684 | tile.attrib["gid"] = str(self.UpdateGIDForRotation(gid + 1, xForm)) 685 | 686 | def MergeTiledFiles(self): 687 | outTree = etree.parse(self.outTiledFile) 688 | outRoot = outTree.getroot() 689 | 690 | # Remove all the properties for "ROOM" from the existing tileset 691 | # and add the room tags for the new tiles to it, if nav tiles 692 | # were generated. 693 | if self.createNavData: 694 | for tileset in outRoot.findall("tileset"): 695 | if tileset.attrib["firstgid"] == "1": 696 | # Remove ROOM property for tiles. 697 | for tile in tileset.findall("tile"): 698 | for properties in tile.findall("properties"): 699 | for property in properties.findall("property"): 700 | if property.attrib["name"] == self.outNavPrefix + MapTiler.PROPERTY_ROOM: 701 | properties.remove(property) 702 | # Add any special properties that we have put into the tiles. 703 | for tileID in self.tileProperties: 704 | tile = etree.SubElement(tileset, "tile") 705 | tile.attrib["id"] = "%s" % tileID 706 | properties = etree.SubElement(tile, "properties") 707 | for name, value in self.tileProperties[tileID]: 708 | property = etree.SubElement(properties, "property") 709 | property.attrib["name"] = name 710 | property.attrib["value"] = "%s" % value 711 | 712 | 713 | # Find all the layers in the output tree 714 | layers = outRoot.findall("layer") 715 | outLayers = { layer.attrib['name']:layer for layer in layers } 716 | # For every layer that already exists, just update the GID data. 717 | # Otherwise, add a new layer to the tree with the data. 718 | for lname in self.layerNames: 719 | if lname in outLayers: 720 | if self.verbose: 721 | print "Layer %s will be updated."%lname 722 | data = outLayers[lname].find("data") 723 | tiles = data.findall("tile") 724 | for idx in xrange(len(tiles)): 725 | tile = tiles[idx] 726 | gid, xForm = self.layerDict[lname][idx] 727 | if (gid == 0): 728 | # This is the "empty" tile 729 | tile.attrib["gid"] = "0" 730 | else: 731 | tile.attrib["gid"] = str(self.UpdateGIDForRotation(gid + 1, xForm)) 732 | else: 733 | # Regardless of "verbosity", let the user know we are 734 | # adding a whole new layer to their map. 735 | print "Layer %s DOES NOT EXIST in the existing file will be added."%lname 736 | self.CreateXMLLayer(outRoot, lname) 737 | return outTree 738 | 739 | def ExportTiledFile(self): 740 | if os.path.exists(self.outTiledFile): 741 | if self.mergeExisting: 742 | outTree = self.MergeTiledFiles() 743 | else: 744 | # Not merging, just overwriting 745 | outTree = self.CreateTiledFile() 746 | else: 747 | outTree = self.CreateTiledFile() 748 | outTree.write(self.outTiledFile, encoding="UTF-8", xml_declaration=True, pretty_print=True) 749 | print "Saving tiled file to %s." % self.outTiledFile 750 | return True 751 | 752 | def SecondsToHMS(self,seconds): 753 | hours = divmod(seconds, 3600) # hours 754 | minutes = divmod(hours[1], 60) # minutes 755 | return (hours[0],minutes[0],minutes[1]) 756 | 757 | # If a merge operation is going to happen, check the existing file 758 | # and find out the layer width/height in tiles. If it is not the 759 | # same, abort the operation. We could check this at the end, but 760 | # this would mean the tile set has been created, and that could 761 | # take a while. 762 | def CheckMergingFiles(self): 763 | if not os.path.exists(self.outTiledFile): 764 | # If the file does not exist, there is nto a problem. 765 | return True 766 | if not self.mergeExisting: 767 | # If we are not merging, there is not a problem. 768 | return True 769 | # Load the existing file. 770 | inTree = etree.parse(self.outTiledFile) 771 | inRoot = inTree.getroot() 772 | 773 | # Verify the tile width/height. 774 | layerWidth = int(inRoot.attrib["width"]) 775 | layerHeight = int(inRoot.attrib["height"]) 776 | if layerWidth != self.layerWidth: 777 | print "Layer width in existing %s = %d which does not match new configuration width %d."%( 778 | self.outTiledFile, 779 | layerWidth, 780 | self.layerWidth) 781 | return False 782 | if layerHeight != self.layerHeight: 783 | print "Layer height in existing %s = %d which does not match new configuration width %d." % ( 784 | self.outTiledFile, 785 | layerHeight, 786 | self.layerHeight) 787 | return False 788 | 789 | # Currently,this script produces only ONE tileset 790 | # and it will take some work to move them all around 791 | # and merge them. So for now at least, the script will 792 | # fail if the tiled file contains more than one tileset. 793 | tilesets = inRoot.findall("tileset") 794 | if len(tilesets) == 0: 795 | print "Existing Tiled file has no tileset in it. Cannot merge." 796 | return False 797 | if len(tilesets) > 1: 798 | print "Existing Tiled file has more than one tileset in it." 799 | return False 800 | images = tilesets[0].findall("image") 801 | if len(images) == 0: 802 | print "Existing tileset has no image associated with it." 803 | return False 804 | if len(images) > 1: 805 | print "Existing tileset is connected to more than one image." 806 | return False 807 | imageSrc = images[0].attrib['source'] 808 | if imageSrc != self.outTilesetFile: 809 | print "Existing tileset uses image %s, not output image %s."%(imageSrc,self.outTilesetFile) 810 | return False 811 | return True 812 | 813 | def CheckExistingFiles(self): 814 | if self.mergeExisting and self.overwriteExisting: 815 | print "Cannot have options to merge and overwrite existing files." 816 | return False 817 | if os.path.exists(self.outTilesetFile): 818 | if not self.mergeExisting and not self.overwriteExisting: 819 | print "Output %s exists and would be modified. Use options to control this."%self.outTilesetFile 820 | return False 821 | if os.path.exists(self.outTiledFile): 822 | if not self.mergeExisting and not self.overwriteExisting: 823 | print "Output %s exists and would be modified. Use options to control this."%self.outTiledFile 824 | return False 825 | return True 826 | 827 | def CheckNavArguments(self): 828 | self.createNavData = False 829 | if self.navPortalLayer or self.navFloorLayer or self.navWallLayer or self.navDoorLayer: 830 | if self.navPortalLayer == None or self.navWallLayer == None or \ 831 | self.navFloorLayer == None or self.navDoorLayer == None: 832 | print "Must specifiy floorLayer, wallLayer, doorLayer, and portalLayer if any are specified." 833 | return False 834 | if self.navFloorLayer not in self.layerNames: 835 | print "Nav Floor Layer %s not in layers."%self.navFloorLayer 836 | return False 837 | if self.navWallLayer not in self.layerNames: 838 | print "Nav Wall Layer %s not in layers." % self.navWallLayer 839 | return False 840 | if self.navPortalLayer not in self.layerNames: 841 | print "Nav Portal Layer %s not in layers." % self.navPortalLayer 842 | return False 843 | self.createNavData = True 844 | return True 845 | 846 | def ProcessInputs(self, 847 | tileWidth, 848 | tileHeight, 849 | tileOffX, 850 | tileOffY, 851 | tileInsetX, 852 | tileInsetY, 853 | fileList, 854 | inputFilePattern, 855 | outTilesetFile, 856 | outTiledFile, 857 | forceSquareTileset, 858 | verbose , 859 | mergeExisting, 860 | overwriteExisting, 861 | floorLayer, 862 | wallLayer, 863 | portalLayer, 864 | doorLayer, 865 | outNavPrefix): 866 | 867 | self.Reset() 868 | 869 | self.tileOffX = tileOffX 870 | self.tileOffY = tileOffY 871 | self.tileInsetX = tileInsetX 872 | self.tileInsetY = tileInsetY 873 | self.tileWidth = tileWidth 874 | self.tileHeight = tileHeight 875 | self.outTilesetFile = outTilesetFile 876 | self.outTiledFile = outTiledFile 877 | self.forceSquareTileset = forceSquareTileset 878 | self.verbose = verbose 879 | self.mergeExisting = mergeExisting 880 | self.overwriteExisting = overwriteExisting 881 | self.startTime = datetime.datetime.now() 882 | self.outNavPrefix = outNavPrefix 883 | self.navWallLayer = wallLayer 884 | self.navPortalLayer = portalLayer 885 | self.navFloorLayer = floorLayer 886 | self.navDoorLayer = doorLayer 887 | 888 | # Main execution path 889 | if not self.CheckExistingFiles(): 890 | print "Unable to continue." 891 | return False 892 | if not self.CreateLayerFiles(inputFilePattern,fileList): 893 | print "Unable to continue." 894 | return False 895 | if not self.CheckNavArguments(): 896 | print "Unable to continue." 897 | return False 898 | if not self.CheckImageSizes(): 899 | print "Unable to continue." 900 | return False 901 | if not self.CheckMergingFiles(): 902 | print "Unable to continue." 903 | return False 904 | if not self.CreateTileset(): 905 | print "Unable to continue." 906 | return False 907 | if not self.CreateNavData(): 908 | print "Unable to continue." 909 | return False 910 | # self.DumpTilemap() 911 | if not self.ExportTileset(): 912 | print "Unable to continue." 913 | return False 914 | if not self.ExportTiledFile(): 915 | print "Unable to continue." 916 | return False 917 | 918 | # Report execution time. 919 | self.stopTime = datetime.datetime.now() 920 | totalSeconds = (self.stopTime-self.startTime).total_seconds() 921 | h,m,s = self.SecondsToHMS(totalSeconds) 922 | print "Started: ",self.startTime 923 | print "Stopped: ",self.stopTime 924 | print "Total Run Time: [%d Hrs: %d Min: %d Sec]"%(h,m,s) 925 | return True 926 | 927 | if __name__ == "__main__": 928 | arguments = docopt.docopt(__doc__) 929 | # When testing is done, this is where 930 | # test arguments are inserted. 931 | """ 932 | arguments[""] = ["Floors.png", 933 | "Walls.png", 934 | "Doors.png", 935 | "DoorActivators.png", 936 | "Portals.png", 937 | "Healing.png", 938 | "Ammo.png", 939 | "Weapons.png"] 940 | arguments["--tileWidth"] = "32" 941 | arguments["--tileHeight"] = "32" 942 | arguments["--verbose"] = True 943 | # arguments["--overwriteExisting"] = True 944 | arguments["--mergeExisting"] = True 945 | arguments["--floorLayer"] = "Floors" 946 | arguments["--wallLayer"] = "Walls" 947 | arguments["--portalLayer"] = "Portals" 948 | arguments["--doorLayer"] = "Doors" 949 | """ 950 | print "-----------------------------------" 951 | print "Inputs:" 952 | args = arguments.keys() 953 | args.sort() 954 | for arg in args: 955 | print "%-25s %s"%(arg,arguments[arg]) 956 | print "-----------------------------------" 957 | tileWidth = int(arguments['--tileWidth']) 958 | tileHeight = int(arguments['--tileHeight']) 959 | tileOffX = int(arguments['--tileOffX']) 960 | tileOffY = int(arguments['--tileOffY']) 961 | tileInsetX = int(arguments['--tileInsetX']) 962 | tileInsetY = int(arguments['--tileInsetY']) 963 | outTiled = arguments['--outTiled'] 964 | outTileset = arguments['--outTileset'] 965 | filePattern = arguments["--filePattern"] 966 | forceSquareTileset = arguments['--forceSquareTileset'] 967 | fileList = arguments[""] 968 | verbose = arguments['--verbose'] 969 | mergeExisting = arguments['--mergeExisting'] 970 | overwriteExisting = arguments['--overwriteExisting'] 971 | wallLayer = arguments['--wallLayer'] 972 | portalLayer = arguments['--portalLayer'] 973 | floorLayer = arguments['--floorLayer'] 974 | doorLayer = arguments['--doorLayer'] 975 | outNavPrefix = arguments['--outNavPrefix'] 976 | 977 | # Now execute the parser 978 | parser = MapTiler() 979 | parser.ProcessInputs(tileWidth, 980 | tileHeight, 981 | tileOffX, 982 | tileOffY, 983 | tileInsetX, 984 | tileInsetY, 985 | fileList, 986 | filePattern, 987 | outTileset, 988 | outTiled, 989 | forceSquareTileset, 990 | verbose, 991 | mergeExisting, 992 | overwriteExisting, 993 | floorLayer, 994 | wallLayer, 995 | portalLayer, 996 | doorLayer, 997 | outNavPrefix) 998 | -------------------------------------------------------------------------------- /NavData.csv: -------------------------------------------------------------------------------- 1 | PKey,SKey,Parameters 2 | Tilemap,File,tiled.tmx 3 | Tilemap,TileHeight,32 4 | Tilemap,TileWidth,32 5 | Tilemap,LayerHeight,23 6 | Tilemap,LayerWidth,28 7 | Tilemap,BlockingLayers,Ammo 8 | Tilemap,BindingLayers,Activators 9 | Graph,Indices,29,30,31,32,33,34,35,36,40,41 10 | Graph,Indices,42,43,44,45,57,58,59,60,61,62 11 | Graph,Indices,63,64,67,68,69,70,71,72,73,74 12 | Graph,Indices,77,78,79,80,81,85,86,87,90,91 13 | Graph,Indices,92,98,99,105,106,107,108,109,110,113 14 | Graph,Indices,114,118,119,120,121,122,123,124,125,126 15 | Graph,Indices,127,128,129,130,131,132,133,134,135,136 16 | Graph,Indices,137,138,141,142,145,146,147,148,149,150 17 | Graph,Indices,151,152,153,154,155,156,157,158,159,160 18 | Graph,Indices,161,162,163,164,165,166,169,170,171,172 19 | Graph,Indices,173,174,175,176,179,180,181,182,183,184 20 | Graph,Indices,185,186,189,190,191,192,193,194,197,198 21 | Graph,Indices,199,200,201,202,203,204,207,208,209,212 22 | Graph,Indices,213,214,220,221,225,226,227,228,229,230 23 | Graph,Indices,231,232,236,237,240,241,248,249,257,258 24 | Graph,Indices,264,265,268,269,276,277,285,286,292,293 25 | Graph,Indices,294,295,296,297,303,304,305,306,313,314 26 | Graph,Indices,320,321,322,323,324,325,331,332,333,334 27 | Graph,Indices,341,342,347,348,349,350,351,352,353,354 28 | Graph,Indices,359,360,361,362,365,366,367,368,369,370 29 | Graph,Indices,375,376,377,378,379,380,381,382,388,389 30 | Graph,Indices,393,394,395,396,397,398,405,406,407,408 31 | Graph,Indices,416,417,421,422,423,424,425,430,431,432 32 | Graph,Indices,433,434,435,436,437,438,439,440,441,444 33 | Graph,Indices,445,449,450,451,452,457,458,459,460,461 34 | Graph,Indices,462,463,464,465,466,467,468,469,470,471 35 | Graph,Indices,472,473,477,478,485,486,487,488,489,490 36 | Graph,Indices,491,492,493,494,495,496,497,500,501,505 37 | Graph,Indices,506,513,521,528,529,533,534,541,549,556 38 | Graph,Indices,557,561,562,563,564,565,566,567,568,569 39 | Graph,Indices,570,571,572,573,574,575,576,577,578,579 40 | Graph,Indices,580,581,582,583,584,585,589,590,591,592 41 | Graph,Indices,593,594,595,596,597,598,599,600,601,602 42 | Graph,Indices,603,604,605,606,607,608,609,610,611,612 43 | Graph,Indices,613 44 | Graph,Adjacent,29,30,0,0,WALK 45 | Graph,Adjacent,29,57,0,0,WALK 46 | Graph,Adjacent,29,58,0,0,WALK 47 | Graph,Adjacent,30,31,0,0,WALK 48 | Graph,Adjacent,30,29,0,0,WALK 49 | Graph,Adjacent,30,58,0,0,WALK 50 | Graph,Adjacent,30,59,0,0,WALK 51 | Graph,Adjacent,30,57,0,0,WALK 52 | Graph,Adjacent,31,32,0,0,WALK 53 | Graph,Adjacent,31,30,0,0,WALK 54 | Graph,Adjacent,31,59,0,0,WALK 55 | Graph,Adjacent,31,60,0,0,WALK 56 | Graph,Adjacent,31,58,0,0,WALK 57 | Graph,Adjacent,32,33,0,0,WALK 58 | Graph,Adjacent,32,31,0,0,WALK 59 | Graph,Adjacent,32,60,0,0,WALK 60 | Graph,Adjacent,32,61,0,0,WALK 61 | Graph,Adjacent,32,59,0,0,WALK 62 | Graph,Adjacent,33,34,0,0,WALK 63 | Graph,Adjacent,33,32,0,0,WALK 64 | Graph,Adjacent,33,61,0,0,WALK 65 | Graph,Adjacent,33,62,0,0,WALK 66 | Graph,Adjacent,33,60,0,0,WALK 67 | Graph,Adjacent,34,35,0,0,WALK 68 | Graph,Adjacent,34,33,0,0,WALK 69 | Graph,Adjacent,34,62,0,0,WALK 70 | Graph,Adjacent,34,63,0,0,WALK 71 | Graph,Adjacent,34,61,0,0,WALK 72 | Graph,Adjacent,35,36,0,0,WALK 73 | Graph,Adjacent,35,34,0,0,WALK 74 | Graph,Adjacent,35,63,0,0,WALK 75 | Graph,Adjacent,35,64,0,0,WALK 76 | Graph,Adjacent,35,62,0,0,WALK 77 | Graph,Adjacent,36,35,0,0,WALK 78 | Graph,Adjacent,36,64,0,0,WALK 79 | Graph,Adjacent,36,63,0,0,WALK 80 | Graph,Adjacent,40,41,1,1,WALK 81 | Graph,Adjacent,40,68,1,1,WALK 82 | Graph,Adjacent,40,69,1,1,WALK 83 | Graph,Adjacent,40,67,1,1,WALK 84 | Graph,Adjacent,41,42,1,1,WALK 85 | Graph,Adjacent,41,40,1,1,WALK 86 | Graph,Adjacent,41,69,1,1,WALK 87 | Graph,Adjacent,41,70,1,1,WALK 88 | Graph,Adjacent,41,68,1,1,WALK 89 | Graph,Adjacent,42,43,1,1,WALK 90 | Graph,Adjacent,42,41,1,1,WALK 91 | Graph,Adjacent,42,70,1,1,WALK 92 | Graph,Adjacent,42,71,1,1,WALK 93 | Graph,Adjacent,42,69,1,1,WALK 94 | Graph,Adjacent,43,44,1,1,WALK 95 | Graph,Adjacent,43,42,1,1,WALK 96 | Graph,Adjacent,43,71,1,1,WALK 97 | Graph,Adjacent,43,72,1,1,WALK 98 | Graph,Adjacent,43,70,1,1,WALK 99 | Graph,Adjacent,44,45,1,1,WALK 100 | Graph,Adjacent,44,43,1,1,WALK 101 | Graph,Adjacent,44,72,1,1,WALK 102 | Graph,Adjacent,44,73,1,1,WALK 103 | Graph,Adjacent,44,71,1,1,WALK 104 | Graph,Adjacent,45,44,1,1,WALK 105 | Graph,Adjacent,45,73,1,1,WALK 106 | Graph,Adjacent,45,74,1,1,WALK 107 | Graph,Adjacent,45,72,1,1,WALK 108 | Graph,Adjacent,57,58,0,0,WALK 109 | Graph,Adjacent,57,85,0,0,WALK 110 | Graph,Adjacent,57,29,0,0,WALK 111 | Graph,Adjacent,57,86,0,0,WALK 112 | Graph,Adjacent,57,30,0,0,WALK 113 | Graph,Adjacent,58,59,0,0,WALK 114 | Graph,Adjacent,58,57,0,0,WALK 115 | Graph,Adjacent,58,86,0,0,WALK 116 | Graph,Adjacent,58,30,0,0,WALK 117 | Graph,Adjacent,58,87,0,0,WALK 118 | Graph,Adjacent,58,31,0,0,WALK 119 | Graph,Adjacent,58,85,0,0,WALK 120 | Graph,Adjacent,58,29,0,0,WALK 121 | Graph,Adjacent,59,60,0,0,WALK 122 | Graph,Adjacent,59,58,0,0,WALK 123 | Graph,Adjacent,59,87,0,0,WALK 124 | Graph,Adjacent,59,31,0,0,WALK 125 | Graph,Adjacent,59,32,0,0,WALK 126 | Graph,Adjacent,59,86,0,0,WALK 127 | Graph,Adjacent,59,30,0,0,WALK 128 | Graph,Adjacent,60,61,0,0,WALK 129 | Graph,Adjacent,60,59,0,0,WALK 130 | Graph,Adjacent,60,32,0,0,WALK 131 | Graph,Adjacent,60,33,0,0,WALK 132 | Graph,Adjacent,60,87,0,0,WALK 133 | Graph,Adjacent,60,31,0,0,WALK 134 | Graph,Adjacent,61,62,0,0,WALK 135 | Graph,Adjacent,61,60,0,0,WALK 136 | Graph,Adjacent,61,33,0,0,WALK 137 | Graph,Adjacent,61,90,0,0,WALK 138 | Graph,Adjacent,61,34,0,0,WALK 139 | Graph,Adjacent,61,32,0,0,WALK 140 | Graph,Adjacent,62,63,0,0,WALK 141 | Graph,Adjacent,62,61,0,0,WALK 142 | Graph,Adjacent,62,90,0,0,WALK 143 | Graph,Adjacent,62,34,0,0,WALK 144 | Graph,Adjacent,62,91,0,0,WALK 145 | Graph,Adjacent,62,35,0,0,WALK 146 | Graph,Adjacent,62,33,0,0,WALK 147 | Graph,Adjacent,63,64,0,0,WALK 148 | Graph,Adjacent,63,62,0,0,WALK 149 | Graph,Adjacent,63,91,0,0,WALK 150 | Graph,Adjacent,63,35,0,0,WALK 151 | Graph,Adjacent,63,92,0,0,WALK 152 | Graph,Adjacent,63,36,0,0,WALK 153 | Graph,Adjacent,63,90,0,0,WALK 154 | Graph,Adjacent,63,34,0,0,WALK 155 | Graph,Adjacent,64,63,0,0,WALK 156 | Graph,Adjacent,64,92,0,0,WALK 157 | Graph,Adjacent,64,36,0,0,WALK 158 | Graph,Adjacent,64,91,0,0,WALK 159 | Graph,Adjacent,64,35,0,0,WALK 160 | Graph,Adjacent,67,68,1,1,WALK 161 | Graph,Adjacent,67,40,1,1,WALK 162 | Graph,Adjacent,68,69,1,1,WALK 163 | Graph,Adjacent,68,67,1,1,WALK 164 | Graph,Adjacent,68,40,1,1,WALK 165 | Graph,Adjacent,68,41,1,1,WALK 166 | Graph,Adjacent,69,70,1,1,WALK 167 | Graph,Adjacent,69,68,1,1,WALK 168 | Graph,Adjacent,69,41,1,1,WALK 169 | Graph,Adjacent,69,98,1,1,WALK 170 | Graph,Adjacent,69,42,1,1,WALK 171 | Graph,Adjacent,69,40,1,1,WALK 172 | Graph,Adjacent,70,71,1,1,WALK 173 | Graph,Adjacent,70,69,1,1,WALK 174 | Graph,Adjacent,70,98,1,1,WALK 175 | Graph,Adjacent,70,42,1,1,WALK 176 | Graph,Adjacent,70,99,1,1,WALK 177 | Graph,Adjacent,70,43,1,1,WALK 178 | Graph,Adjacent,70,41,1,1,WALK 179 | Graph,Adjacent,71,72,1,1,WALK 180 | Graph,Adjacent,71,70,1,1,WALK 181 | Graph,Adjacent,71,99,1,1,WALK 182 | Graph,Adjacent,71,43,1,1,WALK 183 | Graph,Adjacent,71,44,1,1,WALK 184 | Graph,Adjacent,71,98,1,1,WALK 185 | Graph,Adjacent,71,42,1,1,WALK 186 | Graph,Adjacent,72,73,1,1,WALK 187 | Graph,Adjacent,72,71,1,1,WALK 188 | Graph,Adjacent,72,44,1,1,WALK 189 | Graph,Adjacent,72,45,1,1,WALK 190 | Graph,Adjacent,72,99,1,1,WALK 191 | Graph,Adjacent,72,43,1,1,WALK 192 | Graph,Adjacent,73,74,1,1,WALK 193 | Graph,Adjacent,73,72,1,1,WALK 194 | Graph,Adjacent,73,45,1,1,WALK 195 | Graph,Adjacent,73,44,1,1,WALK 196 | Graph,Adjacent,74,73,1,1,WALK 197 | Graph,Adjacent,74,45,1,1,WALK 198 | Graph,Adjacent,77,78,2,2,WALK 199 | Graph,Adjacent,77,105,2,2,WALK 200 | Graph,Adjacent,77,106,2,2,WALK 201 | Graph,Adjacent,78,79,2,2,WALK 202 | Graph,Adjacent,78,77,2,2,WALK 203 | Graph,Adjacent,78,106,2,2,WALK 204 | Graph,Adjacent,78,107,2,2,WALK 205 | Graph,Adjacent,78,105,2,2,WALK 206 | Graph,Adjacent,79,80,2,2,WALK 207 | Graph,Adjacent,79,78,2,2,WALK 208 | Graph,Adjacent,79,107,2,2,WALK 209 | Graph,Adjacent,79,108,2,2,WALK 210 | Graph,Adjacent,79,106,2,2,WALK 211 | Graph,Adjacent,80,81,2,2,WALK 212 | Graph,Adjacent,80,79,2,2,WALK 213 | Graph,Adjacent,80,108,2,2,WALK 214 | Graph,Adjacent,80,107,2,2,WALK 215 | Graph,Adjacent,81,80,2,2,WALK 216 | Graph,Adjacent,81,110,2,2,WALK 217 | Graph,Adjacent,81,108,2,2,WALK 218 | Graph,Adjacent,85,86,0,0,WALK 219 | Graph,Adjacent,85,113,0,0,WALK 220 | Graph,Adjacent,85,57,0,0,WALK 221 | Graph,Adjacent,85,114,0,0,WALK 222 | Graph,Adjacent,85,58,0,0,WALK 223 | Graph,Adjacent,86,87,0,0,WALK 224 | Graph,Adjacent,86,85,0,0,WALK 225 | Graph,Adjacent,86,114,0,0,WALK 226 | Graph,Adjacent,86,58,0,0,WALK 227 | Graph,Adjacent,86,59,0,0,WALK 228 | Graph,Adjacent,86,113,0,0,WALK 229 | Graph,Adjacent,86,57,0,0,WALK 230 | Graph,Adjacent,87,86,0,0,WALK 231 | Graph,Adjacent,87,59,0,0,WALK 232 | Graph,Adjacent,87,60,0,0,WALK 233 | Graph,Adjacent,87,114,0,0,WALK 234 | Graph,Adjacent,87,58,0,0,WALK 235 | Graph,Adjacent,90,91,0,0,WALK 236 | Graph,Adjacent,90,118,0,0,WALK 237 | Graph,Adjacent,90,62,0,0,WALK 238 | Graph,Adjacent,90,119,0,0,WALK 239 | Graph,Adjacent,90,63,0,0,WALK 240 | Graph,Adjacent,90,61,0,0,WALK 241 | Graph,Adjacent,91,92,0,0,WALK 242 | Graph,Adjacent,91,90,0,0,WALK 243 | Graph,Adjacent,91,119,0,0,WALK 244 | Graph,Adjacent,91,63,0,0,WALK 245 | Graph,Adjacent,91,120,0,0,WALK 246 | Graph,Adjacent,91,64,0,0,WALK 247 | Graph,Adjacent,91,118,0,0,WALK 248 | Graph,Adjacent,91,62,0,0,WALK 249 | Graph,Adjacent,92,91,0,0,WALK 250 | Graph,Adjacent,92,120,0,0,WALK 251 | Graph,Adjacent,92,64,0,0,WALK 252 | Graph,Adjacent,92,121,0,0,WALK 253 | Graph,Adjacent,92,119,0,0,WALK 254 | Graph,Adjacent,92,63,0,0,WALK 255 | Graph,Adjacent,98,99,1,1,WALK 256 | Graph,Adjacent,98,126,1,1,WALK 257 | Graph,Adjacent,98,70,1,1,WALK 258 | Graph,Adjacent,98,127,1,1,WALK 259 | Graph,Adjacent,98,71,1,1,WALK 260 | Graph,Adjacent,98,125,1,1,WALK 261 | Graph,Adjacent,98,69,1,1,WALK 262 | Graph,Adjacent,99,98,1,1,WALK 263 | Graph,Adjacent,99,127,1,1,WALK 264 | Graph,Adjacent,99,71,1,1,WALK 265 | Graph,Adjacent,99,128,1,1,WALK 266 | Graph,Adjacent,99,72,1,1,WALK 267 | Graph,Adjacent,99,126,1,1,WALK 268 | Graph,Adjacent,99,70,1,1,WALK 269 | Graph,Adjacent,105,106,2,2,WALK 270 | Graph,Adjacent,105,133,2,2,WALK 271 | Graph,Adjacent,105,77,2,2,WALK 272 | Graph,Adjacent,105,134,2,2,WALK 273 | Graph,Adjacent,105,78,2,2,WALK 274 | Graph,Adjacent,105,132,2,2,WALK 275 | Graph,Adjacent,106,107,2,2,WALK 276 | Graph,Adjacent,106,105,2,2,WALK 277 | Graph,Adjacent,106,134,2,2,WALK 278 | Graph,Adjacent,106,78,2,2,WALK 279 | Graph,Adjacent,106,135,2,2,WALK 280 | Graph,Adjacent,106,79,2,2,WALK 281 | Graph,Adjacent,106,133,2,2,WALK 282 | Graph,Adjacent,106,77,2,2,WALK 283 | Graph,Adjacent,107,108,2,2,WALK 284 | Graph,Adjacent,107,106,2,2,WALK 285 | Graph,Adjacent,107,135,2,2,WALK 286 | Graph,Adjacent,107,79,2,2,WALK 287 | Graph,Adjacent,107,136,2,2,WALK 288 | Graph,Adjacent,107,80,2,2,WALK 289 | Graph,Adjacent,107,134,2,2,WALK 290 | Graph,Adjacent,107,78,2,2,WALK 291 | Graph,Adjacent,108,107,2,2,WALK 292 | Graph,Adjacent,108,136,2,2,WALK 293 | Graph,Adjacent,108,80,2,2,WALK 294 | Graph,Adjacent,108,137,2,2,WALK 295 | Graph,Adjacent,108,81,2,2,WALK 296 | Graph,Adjacent,108,135,2,2,WALK 297 | Graph,Adjacent,108,79,2,2,WALK 298 | Graph,Adjacent,110,138,2,2,WALK 299 | Graph,Adjacent,110,137,2,2,WALK 300 | Graph,Adjacent,110,81,2,2,WALK 301 | Graph,Adjacent,113,114,0,0,WALK 302 | Graph,Adjacent,113,141,0,0,WALK 303 | Graph,Adjacent,113,85,0,0,WALK 304 | Graph,Adjacent,113,142,0,0,WALK 305 | Graph,Adjacent,113,86,0,0,WALK 306 | Graph,Adjacent,114,113,0,0,WALK 307 | Graph,Adjacent,114,142,0,0,WALK 308 | Graph,Adjacent,114,86,0,0,WALK 309 | Graph,Adjacent,114,87,0,0,WALK 310 | Graph,Adjacent,114,141,0,0,WALK 311 | Graph,Adjacent,114,85,0,0,WALK 312 | Graph,Adjacent,118,119,0,0,WALK 313 | Graph,Adjacent,118,146,0,0,WALK 314 | Graph,Adjacent,118,90,0,0,WALK 315 | Graph,Adjacent,118,147,0,0,WALK 316 | Graph,Adjacent,118,91,0,0,WALK 317 | Graph,Adjacent,118,145,0,0,WALK 318 | Graph,Adjacent,119,120,0,0,WALK 319 | Graph,Adjacent,119,118,0,0,WALK 320 | Graph,Adjacent,119,147,0,0,WALK 321 | Graph,Adjacent,119,91,0,0,WALK 322 | Graph,Adjacent,119,148,0,0,WALK 323 | Graph,Adjacent,119,92,0,0,WALK 324 | Graph,Adjacent,119,146,0,0,WALK 325 | Graph,Adjacent,119,90,0,0,WALK 326 | Graph,Adjacent,120,121,0,0,WALK 327 | Graph,Adjacent,120,119,0,0,WALK 328 | Graph,Adjacent,120,148,0,0,WALK 329 | Graph,Adjacent,120,92,0,0,WALK 330 | Graph,Adjacent,120,149,0,0,WALK 331 | Graph,Adjacent,120,147,0,0,WALK 332 | Graph,Adjacent,120,91,0,0,WALK 333 | Graph,Adjacent,121,122,0,1,DOOR 334 | Graph,Adjacent,121,120,0,0,WALK 335 | Graph,Adjacent,121,149,0,0,WALK 336 | Graph,Adjacent,121,150,0,1,DOOR 337 | Graph,Adjacent,121,148,0,0,WALK 338 | Graph,Adjacent,121,92,0,0,WALK 339 | Graph,Adjacent,122,123,1,1,WALK 340 | Graph,Adjacent,122,121,1,0,DOOR 341 | Graph,Adjacent,122,150,1,1,WALK 342 | Graph,Adjacent,122,151,1,1,WALK 343 | Graph,Adjacent,122,149,1,0,DOOR 344 | Graph,Adjacent,123,124,1,1,WALK 345 | Graph,Adjacent,123,122,1,1,WALK 346 | Graph,Adjacent,123,151,1,1,WALK 347 | Graph,Adjacent,123,152,1,1,WALK 348 | Graph,Adjacent,123,150,1,1,WALK 349 | Graph,Adjacent,124,125,1,1,WALK 350 | Graph,Adjacent,124,123,1,1,WALK 351 | Graph,Adjacent,124,152,1,1,WALK 352 | Graph,Adjacent,124,153,1,1,WALK 353 | Graph,Adjacent,124,151,1,1,WALK 354 | Graph,Adjacent,125,126,1,1,WALK 355 | Graph,Adjacent,125,124,1,1,WALK 356 | Graph,Adjacent,125,153,1,1,WALK 357 | Graph,Adjacent,125,154,1,1,WALK 358 | Graph,Adjacent,125,98,1,1,WALK 359 | Graph,Adjacent,125,152,1,1,WALK 360 | Graph,Adjacent,126,127,1,1,WALK 361 | Graph,Adjacent,126,125,1,1,WALK 362 | Graph,Adjacent,126,154,1,1,WALK 363 | Graph,Adjacent,126,98,1,1,WALK 364 | Graph,Adjacent,126,155,1,1,WALK 365 | Graph,Adjacent,126,99,1,1,WALK 366 | Graph,Adjacent,126,153,1,1,WALK 367 | Graph,Adjacent,127,128,1,1,WALK 368 | Graph,Adjacent,127,126,1,1,WALK 369 | Graph,Adjacent,127,155,1,1,WALK 370 | Graph,Adjacent,127,99,1,1,WALK 371 | Graph,Adjacent,127,156,1,1,WALK 372 | Graph,Adjacent,127,154,1,1,WALK 373 | Graph,Adjacent,127,98,1,1,WALK 374 | Graph,Adjacent,128,129,1,1,WALK 375 | Graph,Adjacent,128,127,1,1,WALK 376 | Graph,Adjacent,128,156,1,1,WALK 377 | Graph,Adjacent,128,157,1,1,WALK 378 | Graph,Adjacent,128,155,1,1,WALK 379 | Graph,Adjacent,128,99,1,1,WALK 380 | Graph,Adjacent,129,130,1,1,WALK 381 | Graph,Adjacent,129,128,1,1,WALK 382 | Graph,Adjacent,129,157,1,1,WALK 383 | Graph,Adjacent,129,158,1,1,WALK 384 | Graph,Adjacent,129,156,1,1,WALK 385 | Graph,Adjacent,130,131,1,1,WALK 386 | Graph,Adjacent,130,129,1,1,WALK 387 | Graph,Adjacent,130,158,1,1,WALK 388 | Graph,Adjacent,130,159,1,1,WALK 389 | Graph,Adjacent,130,157,1,1,WALK 390 | Graph,Adjacent,131,132,1,2,DOOR 391 | Graph,Adjacent,131,130,1,1,WALK 392 | Graph,Adjacent,131,159,1,1,WALK 393 | Graph,Adjacent,131,160,1,2,DOOR 394 | Graph,Adjacent,131,158,1,1,WALK 395 | Graph,Adjacent,132,133,2,2,WALK 396 | Graph,Adjacent,132,131,2,1,DOOR 397 | Graph,Adjacent,132,160,2,2,WALK 398 | Graph,Adjacent,132,161,2,2,WALK 399 | Graph,Adjacent,132,105,2,2,WALK 400 | Graph,Adjacent,132,159,2,1,DOOR 401 | Graph,Adjacent,133,134,2,2,WALK 402 | Graph,Adjacent,133,132,2,2,WALK 403 | Graph,Adjacent,133,161,2,2,WALK 404 | Graph,Adjacent,133,105,2,2,WALK 405 | Graph,Adjacent,133,162,2,2,WALK 406 | Graph,Adjacent,133,106,2,2,WALK 407 | Graph,Adjacent,133,160,2,2,WALK 408 | Graph,Adjacent,134,135,2,2,WALK 409 | Graph,Adjacent,134,133,2,2,WALK 410 | Graph,Adjacent,134,162,2,2,WALK 411 | Graph,Adjacent,134,106,2,2,WALK 412 | Graph,Adjacent,134,163,2,2,WALK 413 | Graph,Adjacent,134,107,2,2,WALK 414 | Graph,Adjacent,134,161,2,2,WALK 415 | Graph,Adjacent,134,105,2,2,WALK 416 | Graph,Adjacent,135,136,2,2,WALK 417 | Graph,Adjacent,135,134,2,2,WALK 418 | Graph,Adjacent,135,163,2,2,WALK 419 | Graph,Adjacent,135,107,2,2,WALK 420 | Graph,Adjacent,135,164,2,2,WALK 421 | Graph,Adjacent,135,108,2,2,WALK 422 | Graph,Adjacent,135,162,2,2,WALK 423 | Graph,Adjacent,135,106,2,2,WALK 424 | Graph,Adjacent,136,137,2,2,WALK 425 | Graph,Adjacent,136,135,2,2,WALK 426 | Graph,Adjacent,136,164,2,2,WALK 427 | Graph,Adjacent,136,108,2,2,WALK 428 | Graph,Adjacent,136,165,2,2,WALK 429 | Graph,Adjacent,136,163,2,2,WALK 430 | Graph,Adjacent,136,107,2,2,WALK 431 | Graph,Adjacent,137,138,2,2,WALK 432 | Graph,Adjacent,137,136,2,2,WALK 433 | Graph,Adjacent,137,165,2,2,WALK 434 | Graph,Adjacent,137,166,2,2,WALK 435 | Graph,Adjacent,137,110,2,2,WALK 436 | Graph,Adjacent,137,164,2,2,WALK 437 | Graph,Adjacent,137,108,2,2,WALK 438 | Graph,Adjacent,138,137,2,2,WALK 439 | Graph,Adjacent,138,166,2,2,WALK 440 | Graph,Adjacent,138,110,2,2,WALK 441 | Graph,Adjacent,138,165,2,2,WALK 442 | Graph,Adjacent,141,142,0,0,WALK 443 | Graph,Adjacent,141,169,0,0,WALK 444 | Graph,Adjacent,141,113,0,0,WALK 445 | Graph,Adjacent,141,170,0,0,WALK 446 | Graph,Adjacent,141,114,0,0,WALK 447 | Graph,Adjacent,142,141,0,0,WALK 448 | Graph,Adjacent,142,170,0,0,WALK 449 | Graph,Adjacent,142,114,0,0,WALK 450 | Graph,Adjacent,142,171,0,0,WALK 451 | Graph,Adjacent,142,169,0,0,WALK 452 | Graph,Adjacent,142,113,0,0,WALK 453 | Graph,Adjacent,145,146,0,0,WALK 454 | Graph,Adjacent,145,173,0,0,WALK 455 | Graph,Adjacent,145,174,0,0,WALK 456 | Graph,Adjacent,145,118,0,0,WALK 457 | Graph,Adjacent,145,172,0,0,WALK 458 | Graph,Adjacent,146,147,0,0,WALK 459 | Graph,Adjacent,146,145,0,0,WALK 460 | Graph,Adjacent,146,174,0,0,WALK 461 | Graph,Adjacent,146,118,0,0,WALK 462 | Graph,Adjacent,146,175,0,0,WALK 463 | Graph,Adjacent,146,119,0,0,WALK 464 | Graph,Adjacent,146,173,0,0,WALK 465 | Graph,Adjacent,147,148,0,0,WALK 466 | Graph,Adjacent,147,146,0,0,WALK 467 | Graph,Adjacent,147,175,0,0,WALK 468 | Graph,Adjacent,147,119,0,0,WALK 469 | Graph,Adjacent,147,176,0,0,WALK 470 | Graph,Adjacent,147,120,0,0,WALK 471 | Graph,Adjacent,147,174,0,0,WALK 472 | Graph,Adjacent,147,118,0,0,WALK 473 | Graph,Adjacent,148,149,0,0,WALK 474 | Graph,Adjacent,148,147,0,0,WALK 475 | Graph,Adjacent,148,176,0,0,WALK 476 | Graph,Adjacent,148,120,0,0,WALK 477 | Graph,Adjacent,148,121,0,0,WALK 478 | Graph,Adjacent,148,175,0,0,WALK 479 | Graph,Adjacent,148,119,0,0,WALK 480 | Graph,Adjacent,149,150,0,1,DOOR 481 | Graph,Adjacent,149,148,0,0,WALK 482 | Graph,Adjacent,149,121,0,0,WALK 483 | Graph,Adjacent,149,122,0,1,DOOR 484 | Graph,Adjacent,149,176,0,0,WALK 485 | Graph,Adjacent,149,120,0,0,WALK 486 | Graph,Adjacent,150,151,1,1,WALK 487 | Graph,Adjacent,150,149,1,0,DOOR 488 | Graph,Adjacent,150,122,1,1,WALK 489 | Graph,Adjacent,150,179,1,1,WALK 490 | Graph,Adjacent,150,123,1,1,WALK 491 | Graph,Adjacent,150,121,1,0,DOOR 492 | Graph,Adjacent,151,152,1,1,WALK 493 | Graph,Adjacent,151,150,1,1,WALK 494 | Graph,Adjacent,151,179,1,1,WALK 495 | Graph,Adjacent,151,123,1,1,WALK 496 | Graph,Adjacent,151,180,1,1,WALK 497 | Graph,Adjacent,151,124,1,1,WALK 498 | Graph,Adjacent,151,122,1,1,WALK 499 | Graph,Adjacent,152,153,1,1,WALK 500 | Graph,Adjacent,152,151,1,1,WALK 501 | Graph,Adjacent,152,180,1,1,WALK 502 | Graph,Adjacent,152,124,1,1,WALK 503 | Graph,Adjacent,152,181,1,1,WALK 504 | Graph,Adjacent,152,125,1,1,WALK 505 | Graph,Adjacent,152,179,1,1,WALK 506 | Graph,Adjacent,152,123,1,1,WALK 507 | Graph,Adjacent,153,154,1,1,WALK 508 | Graph,Adjacent,153,152,1,1,WALK 509 | Graph,Adjacent,153,181,1,1,WALK 510 | Graph,Adjacent,153,125,1,1,WALK 511 | Graph,Adjacent,153,182,1,1,WALK 512 | Graph,Adjacent,153,126,1,1,WALK 513 | Graph,Adjacent,153,180,1,1,WALK 514 | Graph,Adjacent,153,124,1,1,WALK 515 | Graph,Adjacent,154,155,1,1,WALK 516 | Graph,Adjacent,154,153,1,1,WALK 517 | Graph,Adjacent,154,182,1,1,WALK 518 | Graph,Adjacent,154,126,1,1,WALK 519 | Graph,Adjacent,154,183,1,1,WALK 520 | Graph,Adjacent,154,127,1,1,WALK 521 | Graph,Adjacent,154,181,1,1,WALK 522 | Graph,Adjacent,154,125,1,1,WALK 523 | Graph,Adjacent,155,156,1,1,WALK 524 | Graph,Adjacent,155,154,1,1,WALK 525 | Graph,Adjacent,155,183,1,1,WALK 526 | Graph,Adjacent,155,127,1,1,WALK 527 | Graph,Adjacent,155,184,1,1,WALK 528 | Graph,Adjacent,155,128,1,1,WALK 529 | Graph,Adjacent,155,182,1,1,WALK 530 | Graph,Adjacent,155,126,1,1,WALK 531 | Graph,Adjacent,156,157,1,1,WALK 532 | Graph,Adjacent,156,155,1,1,WALK 533 | Graph,Adjacent,156,184,1,1,WALK 534 | Graph,Adjacent,156,128,1,1,WALK 535 | Graph,Adjacent,156,185,1,1,WALK 536 | Graph,Adjacent,156,129,1,1,WALK 537 | Graph,Adjacent,156,183,1,1,WALK 538 | Graph,Adjacent,156,127,1,1,WALK 539 | Graph,Adjacent,157,158,1,1,WALK 540 | Graph,Adjacent,157,156,1,1,WALK 541 | Graph,Adjacent,157,185,1,1,WALK 542 | Graph,Adjacent,157,129,1,1,WALK 543 | Graph,Adjacent,157,186,1,1,WALK 544 | Graph,Adjacent,157,130,1,1,WALK 545 | Graph,Adjacent,157,184,1,1,WALK 546 | Graph,Adjacent,157,128,1,1,WALK 547 | Graph,Adjacent,158,159,1,1,WALK 548 | Graph,Adjacent,158,157,1,1,WALK 549 | Graph,Adjacent,158,186,1,1,WALK 550 | Graph,Adjacent,158,130,1,1,WALK 551 | Graph,Adjacent,158,131,1,1,WALK 552 | Graph,Adjacent,158,185,1,1,WALK 553 | Graph,Adjacent,158,129,1,1,WALK 554 | Graph,Adjacent,159,160,1,2,DOOR 555 | Graph,Adjacent,159,158,1,1,WALK 556 | Graph,Adjacent,159,131,1,1,WALK 557 | Graph,Adjacent,159,132,1,2,DOOR 558 | Graph,Adjacent,159,186,1,1,WALK 559 | Graph,Adjacent,159,130,1,1,WALK 560 | Graph,Adjacent,160,161,2,2,WALK 561 | Graph,Adjacent,160,159,2,1,DOOR 562 | Graph,Adjacent,160,132,2,2,WALK 563 | Graph,Adjacent,160,189,2,2,WALK 564 | Graph,Adjacent,160,133,2,2,WALK 565 | Graph,Adjacent,160,131,2,1,DOOR 566 | Graph,Adjacent,161,162,2,2,WALK 567 | Graph,Adjacent,161,160,2,2,WALK 568 | Graph,Adjacent,161,189,2,2,WALK 569 | Graph,Adjacent,161,133,2,2,WALK 570 | Graph,Adjacent,161,190,2,2,WALK 571 | Graph,Adjacent,161,134,2,2,WALK 572 | Graph,Adjacent,161,132,2,2,WALK 573 | Graph,Adjacent,162,163,2,2,WALK 574 | Graph,Adjacent,162,161,2,2,WALK 575 | Graph,Adjacent,162,190,2,2,WALK 576 | Graph,Adjacent,162,134,2,2,WALK 577 | Graph,Adjacent,162,191,2,2,WALK 578 | Graph,Adjacent,162,135,2,2,WALK 579 | Graph,Adjacent,162,189,2,2,WALK 580 | Graph,Adjacent,162,133,2,2,WALK 581 | Graph,Adjacent,163,164,2,2,WALK 582 | Graph,Adjacent,163,162,2,2,WALK 583 | Graph,Adjacent,163,191,2,2,WALK 584 | Graph,Adjacent,163,135,2,2,WALK 585 | Graph,Adjacent,163,192,2,2,WALK 586 | Graph,Adjacent,163,136,2,2,WALK 587 | Graph,Adjacent,163,190,2,2,WALK 588 | Graph,Adjacent,163,134,2,2,WALK 589 | Graph,Adjacent,164,165,2,2,WALK 590 | Graph,Adjacent,164,163,2,2,WALK 591 | Graph,Adjacent,164,192,2,2,WALK 592 | Graph,Adjacent,164,136,2,2,WALK 593 | Graph,Adjacent,164,193,2,2,WALK 594 | Graph,Adjacent,164,137,2,2,WALK 595 | Graph,Adjacent,164,191,2,2,WALK 596 | Graph,Adjacent,164,135,2,2,WALK 597 | Graph,Adjacent,165,166,2,2,WALK 598 | Graph,Adjacent,165,164,2,2,WALK 599 | Graph,Adjacent,165,193,2,2,WALK 600 | Graph,Adjacent,165,137,2,2,WALK 601 | Graph,Adjacent,165,194,2,2,WALK 602 | Graph,Adjacent,165,138,2,2,WALK 603 | Graph,Adjacent,165,192,2,2,WALK 604 | Graph,Adjacent,165,136,2,2,WALK 605 | Graph,Adjacent,166,165,2,2,WALK 606 | Graph,Adjacent,166,194,2,2,WALK 607 | Graph,Adjacent,166,138,2,2,WALK 608 | Graph,Adjacent,166,193,2,2,WALK 609 | Graph,Adjacent,166,137,2,2,WALK 610 | Graph,Adjacent,169,170,0,0,WALK 611 | Graph,Adjacent,169,197,0,0,WALK 612 | Graph,Adjacent,169,141,0,0,WALK 613 | Graph,Adjacent,169,198,0,0,WALK 614 | Graph,Adjacent,169,142,0,0,WALK 615 | Graph,Adjacent,170,171,0,0,WALK 616 | Graph,Adjacent,170,169,0,0,WALK 617 | Graph,Adjacent,170,198,0,0,WALK 618 | Graph,Adjacent,170,142,0,0,WALK 619 | Graph,Adjacent,170,199,0,0,WALK 620 | Graph,Adjacent,170,197,0,0,WALK 621 | Graph,Adjacent,170,141,0,0,WALK 622 | Graph,Adjacent,171,172,0,0,WALK 623 | Graph,Adjacent,171,170,0,0,WALK 624 | Graph,Adjacent,171,199,0,0,WALK 625 | Graph,Adjacent,171,200,0,0,WALK 626 | Graph,Adjacent,171,198,0,0,WALK 627 | Graph,Adjacent,171,142,0,0,WALK 628 | Graph,Adjacent,172,173,0,0,WALK 629 | Graph,Adjacent,172,171,0,0,WALK 630 | Graph,Adjacent,172,200,0,0,WALK 631 | Graph,Adjacent,172,201,0,0,WALK 632 | Graph,Adjacent,172,145,0,0,WALK 633 | Graph,Adjacent,172,199,0,0,WALK 634 | Graph,Adjacent,173,174,0,0,WALK 635 | Graph,Adjacent,173,172,0,0,WALK 636 | Graph,Adjacent,173,201,0,0,WALK 637 | Graph,Adjacent,173,145,0,0,WALK 638 | Graph,Adjacent,173,202,0,0,WALK 639 | Graph,Adjacent,173,146,0,0,WALK 640 | Graph,Adjacent,173,200,0,0,WALK 641 | Graph,Adjacent,174,175,0,0,WALK 642 | Graph,Adjacent,174,173,0,0,WALK 643 | Graph,Adjacent,174,202,0,0,WALK 644 | Graph,Adjacent,174,146,0,0,WALK 645 | Graph,Adjacent,174,203,0,0,WALK 646 | Graph,Adjacent,174,147,0,0,WALK 647 | Graph,Adjacent,174,201,0,0,WALK 648 | Graph,Adjacent,174,145,0,0,WALK 649 | Graph,Adjacent,175,176,0,0,WALK 650 | Graph,Adjacent,175,174,0,0,WALK 651 | Graph,Adjacent,175,203,0,0,WALK 652 | Graph,Adjacent,175,147,0,0,WALK 653 | Graph,Adjacent,175,204,0,0,WALK 654 | Graph,Adjacent,175,148,0,0,WALK 655 | Graph,Adjacent,175,202,0,0,WALK 656 | Graph,Adjacent,175,146,0,0,WALK 657 | Graph,Adjacent,176,175,0,0,WALK 658 | Graph,Adjacent,176,204,0,0,WALK 659 | Graph,Adjacent,176,148,0,0,WALK 660 | Graph,Adjacent,176,149,0,0,WALK 661 | Graph,Adjacent,176,203,0,0,WALK 662 | Graph,Adjacent,176,147,0,0,WALK 663 | Graph,Adjacent,179,180,1,1,WALK 664 | Graph,Adjacent,179,207,1,1,WALK 665 | Graph,Adjacent,179,151,1,1,WALK 666 | Graph,Adjacent,179,208,1,1,WALK 667 | Graph,Adjacent,179,152,1,1,WALK 668 | Graph,Adjacent,179,150,1,1,WALK 669 | Graph,Adjacent,180,181,1,1,WALK 670 | Graph,Adjacent,180,179,1,1,WALK 671 | Graph,Adjacent,180,208,1,1,WALK 672 | Graph,Adjacent,180,152,1,1,WALK 673 | Graph,Adjacent,180,209,1,1,WALK 674 | Graph,Adjacent,180,153,1,1,WALK 675 | Graph,Adjacent,180,207,1,1,WALK 676 | Graph,Adjacent,180,151,1,1,WALK 677 | Graph,Adjacent,181,182,1,1,WALK 678 | Graph,Adjacent,181,180,1,1,WALK 679 | Graph,Adjacent,181,209,1,1,WALK 680 | Graph,Adjacent,181,153,1,1,WALK 681 | Graph,Adjacent,181,154,1,1,WALK 682 | Graph,Adjacent,181,208,1,1,WALK 683 | Graph,Adjacent,181,152,1,1,WALK 684 | Graph,Adjacent,182,183,1,1,WALK 685 | Graph,Adjacent,182,181,1,1,WALK 686 | Graph,Adjacent,182,154,1,1,WALK 687 | Graph,Adjacent,182,155,1,1,WALK 688 | Graph,Adjacent,182,209,1,1,WALK 689 | Graph,Adjacent,182,153,1,1,WALK 690 | Graph,Adjacent,183,184,1,1,WALK 691 | Graph,Adjacent,183,182,1,1,WALK 692 | Graph,Adjacent,183,155,1,1,WALK 693 | Graph,Adjacent,183,212,1,1,WALK 694 | Graph,Adjacent,183,156,1,1,WALK 695 | Graph,Adjacent,183,154,1,1,WALK 696 | Graph,Adjacent,184,185,1,1,WALK 697 | Graph,Adjacent,184,183,1,1,WALK 698 | Graph,Adjacent,184,212,1,1,WALK 699 | Graph,Adjacent,184,156,1,1,WALK 700 | Graph,Adjacent,184,213,1,1,WALK 701 | Graph,Adjacent,184,157,1,1,WALK 702 | Graph,Adjacent,184,155,1,1,WALK 703 | Graph,Adjacent,185,186,1,1,WALK 704 | Graph,Adjacent,185,184,1,1,WALK 705 | Graph,Adjacent,185,213,1,1,WALK 706 | Graph,Adjacent,185,157,1,1,WALK 707 | Graph,Adjacent,185,214,1,1,WALK 708 | Graph,Adjacent,185,158,1,1,WALK 709 | Graph,Adjacent,185,212,1,1,WALK 710 | Graph,Adjacent,185,156,1,1,WALK 711 | Graph,Adjacent,186,185,1,1,WALK 712 | Graph,Adjacent,186,214,1,1,WALK 713 | Graph,Adjacent,186,158,1,1,WALK 714 | Graph,Adjacent,186,159,1,1,WALK 715 | Graph,Adjacent,186,213,1,1,WALK 716 | Graph,Adjacent,186,157,1,1,WALK 717 | Graph,Adjacent,189,190,2,2,WALK 718 | Graph,Adjacent,189,161,2,2,WALK 719 | Graph,Adjacent,189,162,2,2,WALK 720 | Graph,Adjacent,189,160,2,2,WALK 721 | Graph,Adjacent,190,191,2,2,WALK 722 | Graph,Adjacent,190,189,2,2,WALK 723 | Graph,Adjacent,190,162,2,2,WALK 724 | Graph,Adjacent,190,163,2,2,WALK 725 | Graph,Adjacent,190,161,2,2,WALK 726 | Graph,Adjacent,191,192,2,2,WALK 727 | Graph,Adjacent,191,190,2,2,WALK 728 | Graph,Adjacent,191,163,2,2,WALK 729 | Graph,Adjacent,191,220,2,2,WALK 730 | Graph,Adjacent,191,164,2,2,WALK 731 | Graph,Adjacent,191,162,2,2,WALK 732 | Graph,Adjacent,192,193,2,2,WALK 733 | Graph,Adjacent,192,191,2,2,WALK 734 | Graph,Adjacent,192,220,2,2,WALK 735 | Graph,Adjacent,192,164,2,2,WALK 736 | Graph,Adjacent,192,221,2,2,WALK 737 | Graph,Adjacent,192,165,2,2,WALK 738 | Graph,Adjacent,192,163,2,2,WALK 739 | Graph,Adjacent,193,194,2,2,WALK 740 | Graph,Adjacent,193,192,2,2,WALK 741 | Graph,Adjacent,193,221,2,2,WALK 742 | Graph,Adjacent,193,165,2,2,WALK 743 | Graph,Adjacent,193,166,2,2,WALK 744 | Graph,Adjacent,193,220,2,2,WALK 745 | Graph,Adjacent,193,164,2,2,WALK 746 | Graph,Adjacent,194,193,2,2,WALK 747 | Graph,Adjacent,194,166,2,2,WALK 748 | Graph,Adjacent,194,221,2,2,WALK 749 | Graph,Adjacent,194,165,2,2,WALK 750 | Graph,Adjacent,197,198,0,0,WALK 751 | Graph,Adjacent,197,225,0,0,WALK 752 | Graph,Adjacent,197,169,0,0,WALK 753 | Graph,Adjacent,197,226,0,0,WALK 754 | Graph,Adjacent,197,170,0,0,WALK 755 | Graph,Adjacent,198,199,0,0,WALK 756 | Graph,Adjacent,198,197,0,0,WALK 757 | Graph,Adjacent,198,226,0,0,WALK 758 | Graph,Adjacent,198,170,0,0,WALK 759 | Graph,Adjacent,198,227,0,0,WALK 760 | Graph,Adjacent,198,171,0,0,WALK 761 | Graph,Adjacent,198,225,0,0,WALK 762 | Graph,Adjacent,198,169,0,0,WALK 763 | Graph,Adjacent,199,200,0,0,WALK 764 | Graph,Adjacent,199,198,0,0,WALK 765 | Graph,Adjacent,199,227,0,0,WALK 766 | Graph,Adjacent,199,171,0,0,WALK 767 | Graph,Adjacent,199,228,0,0,WALK 768 | Graph,Adjacent,199,172,0,0,WALK 769 | Graph,Adjacent,199,226,0,0,WALK 770 | Graph,Adjacent,199,170,0,0,WALK 771 | Graph,Adjacent,200,201,0,0,WALK 772 | Graph,Adjacent,200,199,0,0,WALK 773 | Graph,Adjacent,200,228,0,0,WALK 774 | Graph,Adjacent,200,172,0,0,WALK 775 | Graph,Adjacent,200,229,0,0,WALK 776 | Graph,Adjacent,200,173,0,0,WALK 777 | Graph,Adjacent,200,227,0,0,WALK 778 | Graph,Adjacent,200,171,0,0,WALK 779 | Graph,Adjacent,201,202,0,0,WALK 780 | Graph,Adjacent,201,200,0,0,WALK 781 | Graph,Adjacent,201,229,0,0,WALK 782 | Graph,Adjacent,201,173,0,0,WALK 783 | Graph,Adjacent,201,230,0,0,WALK 784 | Graph,Adjacent,201,174,0,0,WALK 785 | Graph,Adjacent,201,228,0,0,WALK 786 | Graph,Adjacent,201,172,0,0,WALK 787 | Graph,Adjacent,202,203,0,0,WALK 788 | Graph,Adjacent,202,201,0,0,WALK 789 | Graph,Adjacent,202,230,0,0,WALK 790 | Graph,Adjacent,202,174,0,0,WALK 791 | Graph,Adjacent,202,231,0,0,WALK 792 | Graph,Adjacent,202,175,0,0,WALK 793 | Graph,Adjacent,202,229,0,0,WALK 794 | Graph,Adjacent,202,173,0,0,WALK 795 | Graph,Adjacent,203,204,0,0,WALK 796 | Graph,Adjacent,203,202,0,0,WALK 797 | Graph,Adjacent,203,231,0,0,WALK 798 | Graph,Adjacent,203,175,0,0,WALK 799 | Graph,Adjacent,203,232,0,0,WALK 800 | Graph,Adjacent,203,176,0,0,WALK 801 | Graph,Adjacent,203,230,0,0,WALK 802 | Graph,Adjacent,203,174,0,0,WALK 803 | Graph,Adjacent,204,203,0,0,WALK 804 | Graph,Adjacent,204,232,0,0,WALK 805 | Graph,Adjacent,204,176,0,0,WALK 806 | Graph,Adjacent,204,231,0,0,WALK 807 | Graph,Adjacent,204,175,0,0,WALK 808 | Graph,Adjacent,207,208,1,1,WALK 809 | Graph,Adjacent,207,179,1,1,WALK 810 | Graph,Adjacent,207,236,1,1,WALK 811 | Graph,Adjacent,207,180,1,1,WALK 812 | Graph,Adjacent,208,209,1,1,WALK 813 | Graph,Adjacent,208,207,1,1,WALK 814 | Graph,Adjacent,208,236,1,1,WALK 815 | Graph,Adjacent,208,180,1,1,WALK 816 | Graph,Adjacent,208,237,1,1,WALK 817 | Graph,Adjacent,208,181,1,1,WALK 818 | Graph,Adjacent,208,179,1,1,WALK 819 | Graph,Adjacent,209,208,1,1,WALK 820 | Graph,Adjacent,209,237,1,1,WALK 821 | Graph,Adjacent,209,181,1,1,WALK 822 | Graph,Adjacent,209,182,1,1,WALK 823 | Graph,Adjacent,209,236,1,1,WALK 824 | Graph,Adjacent,209,180,1,1,WALK 825 | Graph,Adjacent,212,213,1,1,WALK 826 | Graph,Adjacent,212,240,1,1,WALK 827 | Graph,Adjacent,212,184,1,1,WALK 828 | Graph,Adjacent,212,241,1,1,WALK 829 | Graph,Adjacent,212,185,1,1,WALK 830 | Graph,Adjacent,212,183,1,1,WALK 831 | Graph,Adjacent,213,214,1,1,WALK 832 | Graph,Adjacent,213,212,1,1,WALK 833 | Graph,Adjacent,213,241,1,1,WALK 834 | Graph,Adjacent,213,185,1,1,WALK 835 | Graph,Adjacent,213,186,1,1,WALK 836 | Graph,Adjacent,213,240,1,1,WALK 837 | Graph,Adjacent,213,184,1,1,WALK 838 | Graph,Adjacent,214,213,1,1,WALK 839 | Graph,Adjacent,214,186,1,1,WALK 840 | Graph,Adjacent,214,241,1,1,WALK 841 | Graph,Adjacent,214,185,1,1,WALK 842 | Graph,Adjacent,220,221,2,2,WALK 843 | Graph,Adjacent,220,248,2,3,DOOR 844 | Graph,Adjacent,220,192,2,2,WALK 845 | Graph,Adjacent,220,249,2,3,DOOR 846 | Graph,Adjacent,220,193,2,2,WALK 847 | Graph,Adjacent,220,191,2,2,WALK 848 | Graph,Adjacent,221,220,2,2,WALK 849 | Graph,Adjacent,221,249,2,3,DOOR 850 | Graph,Adjacent,221,193,2,2,WALK 851 | Graph,Adjacent,221,194,2,2,WALK 852 | Graph,Adjacent,221,248,2,3,DOOR 853 | Graph,Adjacent,221,192,2,2,WALK 854 | Graph,Adjacent,225,226,0,0,WALK 855 | Graph,Adjacent,225,197,0,0,WALK 856 | Graph,Adjacent,225,198,0,0,WALK 857 | Graph,Adjacent,226,227,0,0,WALK 858 | Graph,Adjacent,226,225,0,0,WALK 859 | Graph,Adjacent,226,198,0,0,WALK 860 | Graph,Adjacent,226,199,0,0,WALK 861 | Graph,Adjacent,226,197,0,0,WALK 862 | Graph,Adjacent,227,228,0,0,WALK 863 | Graph,Adjacent,227,226,0,0,WALK 864 | Graph,Adjacent,227,199,0,0,WALK 865 | Graph,Adjacent,227,200,0,0,WALK 866 | Graph,Adjacent,227,198,0,0,WALK 867 | Graph,Adjacent,228,229,0,0,WALK 868 | Graph,Adjacent,228,227,0,0,WALK 869 | Graph,Adjacent,228,200,0,0,WALK 870 | Graph,Adjacent,228,257,0,0,WALK 871 | Graph,Adjacent,228,201,0,0,WALK 872 | Graph,Adjacent,228,199,0,0,WALK 873 | Graph,Adjacent,229,230,0,0,WALK 874 | Graph,Adjacent,229,228,0,0,WALK 875 | Graph,Adjacent,229,257,0,0,WALK 876 | Graph,Adjacent,229,201,0,0,WALK 877 | Graph,Adjacent,229,258,0,0,WALK 878 | Graph,Adjacent,229,202,0,0,WALK 879 | Graph,Adjacent,229,200,0,0,WALK 880 | Graph,Adjacent,230,231,0,0,WALK 881 | Graph,Adjacent,230,229,0,0,WALK 882 | Graph,Adjacent,230,258,0,0,WALK 883 | Graph,Adjacent,230,202,0,0,WALK 884 | Graph,Adjacent,230,203,0,0,WALK 885 | Graph,Adjacent,230,257,0,0,WALK 886 | Graph,Adjacent,230,201,0,0,WALK 887 | Graph,Adjacent,231,232,0,0,WALK 888 | Graph,Adjacent,231,230,0,0,WALK 889 | Graph,Adjacent,231,203,0,0,WALK 890 | Graph,Adjacent,231,204,0,0,WALK 891 | Graph,Adjacent,231,258,0,0,WALK 892 | Graph,Adjacent,231,202,0,0,WALK 893 | Graph,Adjacent,232,231,0,0,WALK 894 | Graph,Adjacent,232,204,0,0,WALK 895 | Graph,Adjacent,232,203,0,0,WALK 896 | Graph,Adjacent,236,237,1,1,WALK 897 | Graph,Adjacent,236,264,1,1,WALK 898 | Graph,Adjacent,236,208,1,1,WALK 899 | Graph,Adjacent,236,265,1,1,WALK 900 | Graph,Adjacent,236,209,1,1,WALK 901 | Graph,Adjacent,236,207,1,1,WALK 902 | Graph,Adjacent,237,236,1,1,WALK 903 | Graph,Adjacent,237,265,1,1,WALK 904 | Graph,Adjacent,237,209,1,1,WALK 905 | Graph,Adjacent,237,264,1,1,WALK 906 | Graph,Adjacent,237,208,1,1,WALK 907 | Graph,Adjacent,240,241,1,1,WALK 908 | Graph,Adjacent,240,268,1,1,WALK 909 | Graph,Adjacent,240,212,1,1,WALK 910 | Graph,Adjacent,240,269,1,1,WALK 911 | Graph,Adjacent,240,213,1,1,WALK 912 | Graph,Adjacent,241,240,1,1,WALK 913 | Graph,Adjacent,241,269,1,1,WALK 914 | Graph,Adjacent,241,213,1,1,WALK 915 | Graph,Adjacent,241,214,1,1,WALK 916 | Graph,Adjacent,241,268,1,1,WALK 917 | Graph,Adjacent,241,212,1,1,WALK 918 | Graph,Adjacent,248,249,3,3,WALK 919 | Graph,Adjacent,248,276,3,3,WALK 920 | Graph,Adjacent,248,220,3,2,DOOR 921 | Graph,Adjacent,248,277,3,3,WALK 922 | Graph,Adjacent,248,221,3,2,DOOR 923 | Graph,Adjacent,249,248,3,3,WALK 924 | Graph,Adjacent,249,277,3,3,WALK 925 | Graph,Adjacent,249,221,3,2,DOOR 926 | Graph,Adjacent,249,276,3,3,WALK 927 | Graph,Adjacent,249,220,3,2,DOOR 928 | Graph,Adjacent,257,258,0,0,WALK 929 | Graph,Adjacent,257,285,0,4,DOOR 930 | Graph,Adjacent,257,229,0,0,WALK 931 | Graph,Adjacent,257,286,0,4,DOOR 932 | Graph,Adjacent,257,230,0,0,WALK 933 | Graph,Adjacent,257,228,0,0,WALK 934 | Graph,Adjacent,258,257,0,0,WALK 935 | Graph,Adjacent,258,286,0,4,DOOR 936 | Graph,Adjacent,258,230,0,0,WALK 937 | Graph,Adjacent,258,231,0,0,WALK 938 | Graph,Adjacent,258,285,0,4,DOOR 939 | Graph,Adjacent,258,229,0,0,WALK 940 | Graph,Adjacent,264,265,1,1,WALK 941 | Graph,Adjacent,264,292,1,1,WALK 942 | Graph,Adjacent,264,236,1,1,WALK 943 | Graph,Adjacent,264,293,1,1,WALK 944 | Graph,Adjacent,264,237,1,1,WALK 945 | Graph,Adjacent,265,264,1,1,WALK 946 | Graph,Adjacent,265,293,1,1,WALK 947 | Graph,Adjacent,265,237,1,1,WALK 948 | Graph,Adjacent,265,294,1,1,WALK 949 | Graph,Adjacent,265,292,1,1,WALK 950 | Graph,Adjacent,265,236,1,1,WALK 951 | Graph,Adjacent,268,269,1,1,WALK 952 | Graph,Adjacent,268,296,1,1,WALK 953 | Graph,Adjacent,268,240,1,1,WALK 954 | Graph,Adjacent,268,297,1,1,WALK 955 | Graph,Adjacent,268,241,1,1,WALK 956 | Graph,Adjacent,268,295,1,1,WALK 957 | Graph,Adjacent,269,268,1,1,WALK 958 | Graph,Adjacent,269,297,1,1,WALK 959 | Graph,Adjacent,269,241,1,1,WALK 960 | Graph,Adjacent,269,296,1,1,WALK 961 | Graph,Adjacent,269,240,1,1,WALK 962 | Graph,Adjacent,276,277,3,3,WALK 963 | Graph,Adjacent,276,304,3,3,WALK 964 | Graph,Adjacent,276,248,3,3,WALK 965 | Graph,Adjacent,276,305,3,3,WALK 966 | Graph,Adjacent,276,249,3,3,WALK 967 | Graph,Adjacent,276,303,3,3,WALK 968 | Graph,Adjacent,277,276,3,3,WALK 969 | Graph,Adjacent,277,305,3,3,WALK 970 | Graph,Adjacent,277,249,3,3,WALK 971 | Graph,Adjacent,277,306,3,3,WALK 972 | Graph,Adjacent,277,304,3,3,WALK 973 | Graph,Adjacent,277,248,3,3,WALK 974 | Graph,Adjacent,285,286,4,4,WALK 975 | Graph,Adjacent,285,313,4,4,WALK 976 | Graph,Adjacent,285,257,4,0,DOOR 977 | Graph,Adjacent,285,314,4,4,WALK 978 | Graph,Adjacent,285,258,4,0,DOOR 979 | Graph,Adjacent,286,285,4,4,WALK 980 | Graph,Adjacent,286,314,4,4,WALK 981 | Graph,Adjacent,286,258,4,0,DOOR 982 | Graph,Adjacent,286,313,4,4,WALK 983 | Graph,Adjacent,286,257,4,0,DOOR 984 | Graph,Adjacent,292,293,1,1,WALK 985 | Graph,Adjacent,292,320,1,1,WALK 986 | Graph,Adjacent,292,264,1,1,WALK 987 | Graph,Adjacent,292,321,1,1,WALK 988 | Graph,Adjacent,292,265,1,1,WALK 989 | Graph,Adjacent,293,294,1,1,WALK 990 | Graph,Adjacent,293,292,1,1,WALK 991 | Graph,Adjacent,293,321,1,1,WALK 992 | Graph,Adjacent,293,265,1,1,WALK 993 | Graph,Adjacent,293,322,1,1,WALK 994 | Graph,Adjacent,293,320,1,1,WALK 995 | Graph,Adjacent,293,264,1,1,WALK 996 | Graph,Adjacent,294,295,1,1,WALK 997 | Graph,Adjacent,294,293,1,1,WALK 998 | Graph,Adjacent,294,322,1,1,WALK 999 | Graph,Adjacent,294,323,1,1,WALK 1000 | Graph,Adjacent,294,321,1,1,WALK 1001 | Graph,Adjacent,294,265,1,1,WALK 1002 | Graph,Adjacent,295,296,1,1,WALK 1003 | Graph,Adjacent,295,294,1,1,WALK 1004 | Graph,Adjacent,295,323,1,1,WALK 1005 | Graph,Adjacent,295,324,1,1,WALK 1006 | Graph,Adjacent,295,268,1,1,WALK 1007 | Graph,Adjacent,295,322,1,1,WALK 1008 | Graph,Adjacent,296,297,1,1,WALK 1009 | Graph,Adjacent,296,295,1,1,WALK 1010 | Graph,Adjacent,296,324,1,1,WALK 1011 | Graph,Adjacent,296,268,1,1,WALK 1012 | Graph,Adjacent,296,325,1,1,WALK 1013 | Graph,Adjacent,296,269,1,1,WALK 1014 | Graph,Adjacent,296,323,1,1,WALK 1015 | Graph,Adjacent,297,296,1,1,WALK 1016 | Graph,Adjacent,297,325,1,1,WALK 1017 | Graph,Adjacent,297,269,1,1,WALK 1018 | Graph,Adjacent,297,324,1,1,WALK 1019 | Graph,Adjacent,297,268,1,1,WALK 1020 | Graph,Adjacent,303,304,3,3,WALK 1021 | Graph,Adjacent,303,331,3,3,WALK 1022 | Graph,Adjacent,303,332,3,3,WALK 1023 | Graph,Adjacent,303,276,3,3,WALK 1024 | Graph,Adjacent,304,305,3,3,WALK 1025 | Graph,Adjacent,304,303,3,3,WALK 1026 | Graph,Adjacent,304,332,3,3,WALK 1027 | Graph,Adjacent,304,276,3,3,WALK 1028 | Graph,Adjacent,304,333,3,3,WALK 1029 | Graph,Adjacent,304,277,3,3,WALK 1030 | Graph,Adjacent,304,331,3,3,WALK 1031 | Graph,Adjacent,305,306,3,3,WALK 1032 | Graph,Adjacent,305,304,3,3,WALK 1033 | Graph,Adjacent,305,333,3,3,WALK 1034 | Graph,Adjacent,305,277,3,3,WALK 1035 | Graph,Adjacent,305,334,3,3,WALK 1036 | Graph,Adjacent,305,332,3,3,WALK 1037 | Graph,Adjacent,305,276,3,3,WALK 1038 | Graph,Adjacent,306,305,3,3,WALK 1039 | Graph,Adjacent,306,334,3,3,WALK 1040 | Graph,Adjacent,306,333,3,3,WALK 1041 | Graph,Adjacent,306,277,3,3,WALK 1042 | Graph,Adjacent,313,314,4,4,WALK 1043 | Graph,Adjacent,313,341,4,4,WALK 1044 | Graph,Adjacent,313,285,4,4,WALK 1045 | Graph,Adjacent,313,342,4,4,WALK 1046 | Graph,Adjacent,313,286,4,4,WALK 1047 | Graph,Adjacent,314,313,4,4,WALK 1048 | Graph,Adjacent,314,342,4,4,WALK 1049 | Graph,Adjacent,314,286,4,4,WALK 1050 | Graph,Adjacent,314,341,4,4,WALK 1051 | Graph,Adjacent,314,285,4,4,WALK 1052 | Graph,Adjacent,320,321,1,1,WALK 1053 | Graph,Adjacent,320,348,1,1,WALK 1054 | Graph,Adjacent,320,292,1,1,WALK 1055 | Graph,Adjacent,320,349,1,1,WALK 1056 | Graph,Adjacent,320,293,1,1,WALK 1057 | Graph,Adjacent,320,347,1,1,WALK 1058 | Graph,Adjacent,321,322,1,1,WALK 1059 | Graph,Adjacent,321,320,1,1,WALK 1060 | Graph,Adjacent,321,349,1,1,WALK 1061 | Graph,Adjacent,321,293,1,1,WALK 1062 | Graph,Adjacent,321,350,1,1,WALK 1063 | Graph,Adjacent,321,294,1,1,WALK 1064 | Graph,Adjacent,321,348,1,1,WALK 1065 | Graph,Adjacent,321,292,1,1,WALK 1066 | Graph,Adjacent,322,323,1,1,WALK 1067 | Graph,Adjacent,322,321,1,1,WALK 1068 | Graph,Adjacent,322,350,1,1,WALK 1069 | Graph,Adjacent,322,294,1,1,WALK 1070 | Graph,Adjacent,322,351,1,1,WALK 1071 | Graph,Adjacent,322,295,1,1,WALK 1072 | Graph,Adjacent,322,349,1,1,WALK 1073 | Graph,Adjacent,322,293,1,1,WALK 1074 | Graph,Adjacent,323,324,1,1,WALK 1075 | Graph,Adjacent,323,322,1,1,WALK 1076 | Graph,Adjacent,323,351,1,1,WALK 1077 | Graph,Adjacent,323,295,1,1,WALK 1078 | Graph,Adjacent,323,352,1,1,WALK 1079 | Graph,Adjacent,323,296,1,1,WALK 1080 | Graph,Adjacent,323,350,1,1,WALK 1081 | Graph,Adjacent,323,294,1,1,WALK 1082 | Graph,Adjacent,324,325,1,1,WALK 1083 | Graph,Adjacent,324,323,1,1,WALK 1084 | Graph,Adjacent,324,352,1,1,WALK 1085 | Graph,Adjacent,324,296,1,1,WALK 1086 | Graph,Adjacent,324,353,1,1,WALK 1087 | Graph,Adjacent,324,297,1,1,WALK 1088 | Graph,Adjacent,324,351,1,1,WALK 1089 | Graph,Adjacent,324,295,1,1,WALK 1090 | Graph,Adjacent,325,324,1,1,WALK 1091 | Graph,Adjacent,325,353,1,1,WALK 1092 | Graph,Adjacent,325,297,1,1,WALK 1093 | Graph,Adjacent,325,354,1,1,WALK 1094 | Graph,Adjacent,325,352,1,1,WALK 1095 | Graph,Adjacent,325,296,1,1,WALK 1096 | Graph,Adjacent,331,332,3,3,WALK 1097 | Graph,Adjacent,331,359,3,3,WALK 1098 | Graph,Adjacent,331,303,3,3,WALK 1099 | Graph,Adjacent,331,360,3,3,WALK 1100 | Graph,Adjacent,331,304,3,3,WALK 1101 | Graph,Adjacent,332,333,3,3,WALK 1102 | Graph,Adjacent,332,331,3,3,WALK 1103 | Graph,Adjacent,332,360,3,3,WALK 1104 | Graph,Adjacent,332,304,3,3,WALK 1105 | Graph,Adjacent,332,361,3,3,WALK 1106 | Graph,Adjacent,332,305,3,3,WALK 1107 | Graph,Adjacent,332,359,3,3,WALK 1108 | Graph,Adjacent,332,303,3,3,WALK 1109 | Graph,Adjacent,333,334,3,3,WALK 1110 | Graph,Adjacent,333,332,3,3,WALK 1111 | Graph,Adjacent,333,361,3,3,WALK 1112 | Graph,Adjacent,333,305,3,3,WALK 1113 | Graph,Adjacent,333,362,3,3,WALK 1114 | Graph,Adjacent,333,306,3,3,WALK 1115 | Graph,Adjacent,333,360,3,3,WALK 1116 | Graph,Adjacent,333,304,3,3,WALK 1117 | Graph,Adjacent,334,333,3,3,WALK 1118 | Graph,Adjacent,334,362,3,3,WALK 1119 | Graph,Adjacent,334,306,3,3,WALK 1120 | Graph,Adjacent,334,361,3,3,WALK 1121 | Graph,Adjacent,334,305,3,3,WALK 1122 | Graph,Adjacent,341,342,4,4,WALK 1123 | Graph,Adjacent,341,369,4,4,WALK 1124 | Graph,Adjacent,341,313,4,4,WALK 1125 | Graph,Adjacent,341,370,4,4,WALK 1126 | Graph,Adjacent,341,314,4,4,WALK 1127 | Graph,Adjacent,341,368,4,4,WALK 1128 | Graph,Adjacent,342,341,4,4,WALK 1129 | Graph,Adjacent,342,370,4,4,WALK 1130 | Graph,Adjacent,342,314,4,4,WALK 1131 | Graph,Adjacent,342,369,4,4,WALK 1132 | Graph,Adjacent,342,313,4,4,WALK 1133 | Graph,Adjacent,347,348,1,1,WALK 1134 | Graph,Adjacent,347,375,1,1,WALK 1135 | Graph,Adjacent,347,376,1,1,WALK 1136 | Graph,Adjacent,347,320,1,1,WALK 1137 | Graph,Adjacent,348,349,1,1,WALK 1138 | Graph,Adjacent,348,347,1,1,WALK 1139 | Graph,Adjacent,348,376,1,1,WALK 1140 | Graph,Adjacent,348,320,1,1,WALK 1141 | Graph,Adjacent,348,377,1,1,WALK 1142 | Graph,Adjacent,348,321,1,1,WALK 1143 | Graph,Adjacent,348,375,1,1,WALK 1144 | Graph,Adjacent,349,350,1,1,WALK 1145 | Graph,Adjacent,349,348,1,1,WALK 1146 | Graph,Adjacent,349,377,1,1,WALK 1147 | Graph,Adjacent,349,321,1,1,WALK 1148 | Graph,Adjacent,349,378,1,1,WALK 1149 | Graph,Adjacent,349,322,1,1,WALK 1150 | Graph,Adjacent,349,376,1,1,WALK 1151 | Graph,Adjacent,349,320,1,1,WALK 1152 | Graph,Adjacent,350,351,1,1,WALK 1153 | Graph,Adjacent,350,349,1,1,WALK 1154 | Graph,Adjacent,350,378,1,1,WALK 1155 | Graph,Adjacent,350,322,1,1,WALK 1156 | Graph,Adjacent,350,379,1,1,WALK 1157 | Graph,Adjacent,350,323,1,1,WALK 1158 | Graph,Adjacent,350,377,1,1,WALK 1159 | Graph,Adjacent,350,321,1,1,WALK 1160 | Graph,Adjacent,351,352,1,1,WALK 1161 | Graph,Adjacent,351,350,1,1,WALK 1162 | Graph,Adjacent,351,379,1,1,WALK 1163 | Graph,Adjacent,351,323,1,1,WALK 1164 | Graph,Adjacent,351,380,1,1,WALK 1165 | Graph,Adjacent,351,324,1,1,WALK 1166 | Graph,Adjacent,351,378,1,1,WALK 1167 | Graph,Adjacent,351,322,1,1,WALK 1168 | Graph,Adjacent,352,353,1,1,WALK 1169 | Graph,Adjacent,352,351,1,1,WALK 1170 | Graph,Adjacent,352,380,1,1,WALK 1171 | Graph,Adjacent,352,324,1,1,WALK 1172 | Graph,Adjacent,352,381,1,1,WALK 1173 | Graph,Adjacent,352,325,1,1,WALK 1174 | Graph,Adjacent,352,379,1,1,WALK 1175 | Graph,Adjacent,352,323,1,1,WALK 1176 | Graph,Adjacent,353,354,1,1,WALK 1177 | Graph,Adjacent,353,352,1,1,WALK 1178 | Graph,Adjacent,353,381,1,1,WALK 1179 | Graph,Adjacent,353,325,1,1,WALK 1180 | Graph,Adjacent,353,382,1,1,WALK 1181 | Graph,Adjacent,353,380,1,1,WALK 1182 | Graph,Adjacent,353,324,1,1,WALK 1183 | Graph,Adjacent,354,353,1,1,WALK 1184 | Graph,Adjacent,354,382,1,1,WALK 1185 | Graph,Adjacent,354,381,1,1,WALK 1186 | Graph,Adjacent,354,325,1,1,WALK 1187 | Graph,Adjacent,359,360,3,3,WALK 1188 | Graph,Adjacent,359,331,3,3,WALK 1189 | Graph,Adjacent,359,388,3,3,WALK 1190 | Graph,Adjacent,359,332,3,3,WALK 1191 | Graph,Adjacent,360,361,3,3,WALK 1192 | Graph,Adjacent,360,359,3,3,WALK 1193 | Graph,Adjacent,360,388,3,3,WALK 1194 | Graph,Adjacent,360,332,3,3,WALK 1195 | Graph,Adjacent,360,389,3,3,WALK 1196 | Graph,Adjacent,360,333,3,3,WALK 1197 | Graph,Adjacent,360,331,3,3,WALK 1198 | Graph,Adjacent,361,362,3,3,WALK 1199 | Graph,Adjacent,361,360,3,3,WALK 1200 | Graph,Adjacent,361,389,3,3,WALK 1201 | Graph,Adjacent,361,333,3,3,WALK 1202 | Graph,Adjacent,361,334,3,3,WALK 1203 | Graph,Adjacent,361,388,3,3,WALK 1204 | Graph,Adjacent,361,332,3,3,WALK 1205 | Graph,Adjacent,362,361,3,3,WALK 1206 | Graph,Adjacent,362,334,3,3,WALK 1207 | Graph,Adjacent,362,389,3,3,WALK 1208 | Graph,Adjacent,362,333,3,3,WALK 1209 | Graph,Adjacent,365,366,4,4,WALK 1210 | Graph,Adjacent,365,393,4,4,WALK 1211 | Graph,Adjacent,366,367,4,4,WALK 1212 | Graph,Adjacent,366,365,4,4,WALK 1213 | Graph,Adjacent,366,395,4,4,WALK 1214 | Graph,Adjacent,366,393,4,4,WALK 1215 | Graph,Adjacent,367,368,4,4,WALK 1216 | Graph,Adjacent,367,366,4,4,WALK 1217 | Graph,Adjacent,367,395,4,4,WALK 1218 | Graph,Adjacent,367,396,4,4,WALK 1219 | Graph,Adjacent,368,369,4,4,WALK 1220 | Graph,Adjacent,368,367,4,4,WALK 1221 | Graph,Adjacent,368,396,4,4,WALK 1222 | Graph,Adjacent,368,397,4,4,WALK 1223 | Graph,Adjacent,368,341,4,4,WALK 1224 | Graph,Adjacent,368,395,4,4,WALK 1225 | Graph,Adjacent,369,370,4,4,WALK 1226 | Graph,Adjacent,369,368,4,4,WALK 1227 | Graph,Adjacent,369,397,4,4,WALK 1228 | Graph,Adjacent,369,341,4,4,WALK 1229 | Graph,Adjacent,369,398,4,4,WALK 1230 | Graph,Adjacent,369,342,4,4,WALK 1231 | Graph,Adjacent,369,396,4,4,WALK 1232 | Graph,Adjacent,370,369,4,4,WALK 1233 | Graph,Adjacent,370,398,4,4,WALK 1234 | Graph,Adjacent,370,342,4,4,WALK 1235 | Graph,Adjacent,370,397,4,4,WALK 1236 | Graph,Adjacent,370,341,4,4,WALK 1237 | Graph,Adjacent,375,376,1,1,WALK 1238 | Graph,Adjacent,375,347,1,1,WALK 1239 | Graph,Adjacent,375,348,1,1,WALK 1240 | Graph,Adjacent,376,377,1,1,WALK 1241 | Graph,Adjacent,376,375,1,1,WALK 1242 | Graph,Adjacent,376,348,1,1,WALK 1243 | Graph,Adjacent,376,405,1,5,ROOM 1244 | Graph,Adjacent,376,349,1,1,WALK 1245 | Graph,Adjacent,376,347,1,1,WALK 1246 | Graph,Adjacent,377,378,1,1,WALK 1247 | Graph,Adjacent,377,376,1,1,WALK 1248 | Graph,Adjacent,377,405,1,5,ROOM 1249 | Graph,Adjacent,377,349,1,1,WALK 1250 | Graph,Adjacent,377,406,1,5,ROOM 1251 | Graph,Adjacent,377,350,1,1,WALK 1252 | Graph,Adjacent,377,348,1,1,WALK 1253 | Graph,Adjacent,378,379,1,1,WALK 1254 | Graph,Adjacent,378,377,1,1,WALK 1255 | Graph,Adjacent,378,406,1,5,ROOM 1256 | Graph,Adjacent,378,350,1,1,WALK 1257 | Graph,Adjacent,378,407,1,5,ROOM 1258 | Graph,Adjacent,378,351,1,1,WALK 1259 | Graph,Adjacent,378,405,1,5,ROOM 1260 | Graph,Adjacent,378,349,1,1,WALK 1261 | Graph,Adjacent,379,380,1,1,WALK 1262 | Graph,Adjacent,379,378,1,1,WALK 1263 | Graph,Adjacent,379,407,1,5,ROOM 1264 | Graph,Adjacent,379,351,1,1,WALK 1265 | Graph,Adjacent,379,408,1,5,ROOM 1266 | Graph,Adjacent,379,352,1,1,WALK 1267 | Graph,Adjacent,379,406,1,5,ROOM 1268 | Graph,Adjacent,379,350,1,1,WALK 1269 | Graph,Adjacent,380,381,1,1,WALK 1270 | Graph,Adjacent,380,379,1,1,WALK 1271 | Graph,Adjacent,380,408,1,5,ROOM 1272 | Graph,Adjacent,380,352,1,1,WALK 1273 | Graph,Adjacent,380,353,1,1,WALK 1274 | Graph,Adjacent,380,407,1,5,ROOM 1275 | Graph,Adjacent,380,351,1,1,WALK 1276 | Graph,Adjacent,381,382,1,1,WALK 1277 | Graph,Adjacent,381,380,1,1,WALK 1278 | Graph,Adjacent,381,353,1,1,WALK 1279 | Graph,Adjacent,381,354,1,1,WALK 1280 | Graph,Adjacent,381,408,1,5,ROOM 1281 | Graph,Adjacent,381,352,1,1,WALK 1282 | Graph,Adjacent,382,381,1,1,WALK 1283 | Graph,Adjacent,382,354,1,1,WALK 1284 | Graph,Adjacent,382,353,1,1,WALK 1285 | Graph,Adjacent,388,389,3,3,WALK 1286 | Graph,Adjacent,388,416,3,3,WALK 1287 | Graph,Adjacent,388,360,3,3,WALK 1288 | Graph,Adjacent,388,417,3,3,WALK 1289 | Graph,Adjacent,388,361,3,3,WALK 1290 | Graph,Adjacent,388,359,3,3,WALK 1291 | Graph,Adjacent,389,388,3,3,WALK 1292 | Graph,Adjacent,389,417,3,3,WALK 1293 | Graph,Adjacent,389,361,3,3,WALK 1294 | Graph,Adjacent,389,362,3,3,WALK 1295 | Graph,Adjacent,389,416,3,3,WALK 1296 | Graph,Adjacent,389,360,3,3,WALK 1297 | Graph,Adjacent,393,421,4,4,WALK 1298 | Graph,Adjacent,393,365,4,4,WALK 1299 | Graph,Adjacent,393,422,4,4,WALK 1300 | Graph,Adjacent,393,366,4,4,WALK 1301 | Graph,Adjacent,395,396,4,4,WALK 1302 | Graph,Adjacent,395,423,4,4,WALK 1303 | Graph,Adjacent,395,367,4,4,WALK 1304 | Graph,Adjacent,395,424,4,4,WALK 1305 | Graph,Adjacent,395,368,4,4,WALK 1306 | Graph,Adjacent,395,422,4,4,WALK 1307 | Graph,Adjacent,395,366,4,4,WALK 1308 | Graph,Adjacent,396,397,4,4,WALK 1309 | Graph,Adjacent,396,395,4,4,WALK 1310 | Graph,Adjacent,396,424,4,4,WALK 1311 | Graph,Adjacent,396,368,4,4,WALK 1312 | Graph,Adjacent,396,425,4,4,WALK 1313 | Graph,Adjacent,396,369,4,4,WALK 1314 | Graph,Adjacent,396,423,4,4,WALK 1315 | Graph,Adjacent,396,367,4,4,WALK 1316 | Graph,Adjacent,397,398,4,4,WALK 1317 | Graph,Adjacent,397,396,4,4,WALK 1318 | Graph,Adjacent,397,425,4,4,WALK 1319 | Graph,Adjacent,397,369,4,4,WALK 1320 | Graph,Adjacent,397,370,4,4,WALK 1321 | Graph,Adjacent,397,424,4,4,WALK 1322 | Graph,Adjacent,397,368,4,4,WALK 1323 | Graph,Adjacent,398,397,4,4,WALK 1324 | Graph,Adjacent,398,370,4,4,WALK 1325 | Graph,Adjacent,398,425,4,4,WALK 1326 | Graph,Adjacent,398,369,4,4,WALK 1327 | Graph,Adjacent,405,406,5,5,WALK 1328 | Graph,Adjacent,405,433,5,5,WALK 1329 | Graph,Adjacent,405,377,5,1,ROOM 1330 | Graph,Adjacent,405,434,5,5,WALK 1331 | Graph,Adjacent,405,378,5,1,ROOM 1332 | Graph,Adjacent,405,432,5,5,WALK 1333 | Graph,Adjacent,405,376,5,1,ROOM 1334 | Graph,Adjacent,406,407,5,5,WALK 1335 | Graph,Adjacent,406,405,5,5,WALK 1336 | Graph,Adjacent,406,434,5,5,WALK 1337 | Graph,Adjacent,406,378,5,1,ROOM 1338 | Graph,Adjacent,406,435,5,5,WALK 1339 | Graph,Adjacent,406,379,5,1,ROOM 1340 | Graph,Adjacent,406,433,5,5,WALK 1341 | Graph,Adjacent,406,377,5,1,ROOM 1342 | Graph,Adjacent,407,408,5,5,WALK 1343 | Graph,Adjacent,407,406,5,5,WALK 1344 | Graph,Adjacent,407,435,5,5,WALK 1345 | Graph,Adjacent,407,379,5,1,ROOM 1346 | Graph,Adjacent,407,436,5,5,WALK 1347 | Graph,Adjacent,407,380,5,1,ROOM 1348 | Graph,Adjacent,407,434,5,5,WALK 1349 | Graph,Adjacent,407,378,5,1,ROOM 1350 | Graph,Adjacent,408,407,5,5,WALK 1351 | Graph,Adjacent,408,436,5,5,WALK 1352 | Graph,Adjacent,408,380,5,1,ROOM 1353 | Graph,Adjacent,408,437,5,5,WALK 1354 | Graph,Adjacent,408,381,5,1,ROOM 1355 | Graph,Adjacent,408,435,5,5,WALK 1356 | Graph,Adjacent,408,379,5,1,ROOM 1357 | Graph,Adjacent,416,417,3,3,WALK 1358 | Graph,Adjacent,416,444,3,6,DOOR 1359 | Graph,Adjacent,416,388,3,3,WALK 1360 | Graph,Adjacent,416,445,3,6,DOOR 1361 | Graph,Adjacent,416,389,3,3,WALK 1362 | Graph,Adjacent,417,416,3,3,WALK 1363 | Graph,Adjacent,417,445,3,6,DOOR 1364 | Graph,Adjacent,417,389,3,3,WALK 1365 | Graph,Adjacent,417,444,3,6,DOOR 1366 | Graph,Adjacent,417,388,3,3,WALK 1367 | Graph,Adjacent,421,422,4,4,WALK 1368 | Graph,Adjacent,421,449,4,4,WALK 1369 | Graph,Adjacent,421,393,4,4,WALK 1370 | Graph,Adjacent,421,450,4,4,WALK 1371 | Graph,Adjacent,422,423,4,4,WALK 1372 | Graph,Adjacent,422,421,4,4,WALK 1373 | Graph,Adjacent,422,450,4,4,WALK 1374 | Graph,Adjacent,422,451,4,4,WALK 1375 | Graph,Adjacent,422,395,4,4,WALK 1376 | Graph,Adjacent,422,449,4,4,WALK 1377 | Graph,Adjacent,422,393,4,4,WALK 1378 | Graph,Adjacent,423,424,4,4,WALK 1379 | Graph,Adjacent,423,422,4,4,WALK 1380 | Graph,Adjacent,423,451,4,4,WALK 1381 | Graph,Adjacent,423,395,4,4,WALK 1382 | Graph,Adjacent,423,452,4,4,WALK 1383 | Graph,Adjacent,423,396,4,4,WALK 1384 | Graph,Adjacent,423,450,4,4,WALK 1385 | Graph,Adjacent,424,425,4,4,WALK 1386 | Graph,Adjacent,424,423,4,4,WALK 1387 | Graph,Adjacent,424,452,4,4,WALK 1388 | Graph,Adjacent,424,396,4,4,WALK 1389 | Graph,Adjacent,424,397,4,4,WALK 1390 | Graph,Adjacent,424,451,4,4,WALK 1391 | Graph,Adjacent,424,395,4,4,WALK 1392 | Graph,Adjacent,425,424,4,4,WALK 1393 | Graph,Adjacent,425,397,4,4,WALK 1394 | Graph,Adjacent,425,398,4,4,WALK 1395 | Graph,Adjacent,425,452,4,4,WALK 1396 | Graph,Adjacent,425,396,4,4,WALK 1397 | Graph,Adjacent,430,431,5,5,WALK 1398 | Graph,Adjacent,430,459,5,5,WALK 1399 | Graph,Adjacent,430,457,5,5,WALK 1400 | Graph,Adjacent,431,432,5,5,WALK 1401 | Graph,Adjacent,431,430,5,5,WALK 1402 | Graph,Adjacent,431,459,5,5,WALK 1403 | Graph,Adjacent,431,460,5,5,WALK 1404 | Graph,Adjacent,432,433,5,5,WALK 1405 | Graph,Adjacent,432,431,5,5,WALK 1406 | Graph,Adjacent,432,460,5,5,WALK 1407 | Graph,Adjacent,432,461,5,5,WALK 1408 | Graph,Adjacent,432,405,5,5,WALK 1409 | Graph,Adjacent,432,459,5,5,WALK 1410 | Graph,Adjacent,433,434,5,5,WALK 1411 | Graph,Adjacent,433,432,5,5,WALK 1412 | Graph,Adjacent,433,461,5,5,WALK 1413 | Graph,Adjacent,433,405,5,5,WALK 1414 | Graph,Adjacent,433,462,5,5,WALK 1415 | Graph,Adjacent,433,406,5,5,WALK 1416 | Graph,Adjacent,433,460,5,5,WALK 1417 | Graph,Adjacent,434,435,5,5,WALK 1418 | Graph,Adjacent,434,433,5,5,WALK 1419 | Graph,Adjacent,434,462,5,5,WALK 1420 | Graph,Adjacent,434,406,5,5,WALK 1421 | Graph,Adjacent,434,463,5,5,WALK 1422 | Graph,Adjacent,434,407,5,5,WALK 1423 | Graph,Adjacent,434,461,5,5,WALK 1424 | Graph,Adjacent,434,405,5,5,WALK 1425 | Graph,Adjacent,435,436,5,5,WALK 1426 | Graph,Adjacent,435,434,5,5,WALK 1427 | Graph,Adjacent,435,463,5,5,WALK 1428 | Graph,Adjacent,435,407,5,5,WALK 1429 | Graph,Adjacent,435,464,5,5,WALK 1430 | Graph,Adjacent,435,408,5,5,WALK 1431 | Graph,Adjacent,435,462,5,5,WALK 1432 | Graph,Adjacent,435,406,5,5,WALK 1433 | Graph,Adjacent,436,437,5,5,WALK 1434 | Graph,Adjacent,436,435,5,5,WALK 1435 | Graph,Adjacent,436,464,5,5,WALK 1436 | Graph,Adjacent,436,408,5,5,WALK 1437 | Graph,Adjacent,436,465,5,5,WALK 1438 | Graph,Adjacent,436,463,5,5,WALK 1439 | Graph,Adjacent,436,407,5,5,WALK 1440 | Graph,Adjacent,437,438,5,5,WALK 1441 | Graph,Adjacent,437,436,5,5,WALK 1442 | Graph,Adjacent,437,465,5,5,WALK 1443 | Graph,Adjacent,437,466,5,5,WALK 1444 | Graph,Adjacent,437,464,5,5,WALK 1445 | Graph,Adjacent,437,408,5,5,WALK 1446 | Graph,Adjacent,438,439,5,5,WALK 1447 | Graph,Adjacent,438,437,5,5,WALK 1448 | Graph,Adjacent,438,466,5,5,WALK 1449 | Graph,Adjacent,438,467,5,5,WALK 1450 | Graph,Adjacent,438,465,5,5,WALK 1451 | Graph,Adjacent,439,440,5,5,WALK 1452 | Graph,Adjacent,439,438,5,5,WALK 1453 | Graph,Adjacent,439,467,5,5,WALK 1454 | Graph,Adjacent,439,468,5,5,WALK 1455 | Graph,Adjacent,439,466,5,5,WALK 1456 | Graph,Adjacent,440,441,5,5,WALK 1457 | Graph,Adjacent,440,439,5,5,WALK 1458 | Graph,Adjacent,440,468,5,5,WALK 1459 | Graph,Adjacent,440,469,5,5,WALK 1460 | Graph,Adjacent,440,467,5,5,WALK 1461 | Graph,Adjacent,441,440,5,5,WALK 1462 | Graph,Adjacent,441,469,5,5,WALK 1463 | Graph,Adjacent,441,470,5,5,WALK 1464 | Graph,Adjacent,441,468,5,5,WALK 1465 | Graph,Adjacent,444,445,6,6,WALK 1466 | Graph,Adjacent,444,472,6,6,WALK 1467 | Graph,Adjacent,444,416,6,3,DOOR 1468 | Graph,Adjacent,444,473,6,6,WALK 1469 | Graph,Adjacent,444,417,6,3,DOOR 1470 | Graph,Adjacent,444,471,6,6,WALK 1471 | Graph,Adjacent,445,444,6,6,WALK 1472 | Graph,Adjacent,445,473,6,6,WALK 1473 | Graph,Adjacent,445,417,6,3,DOOR 1474 | Graph,Adjacent,445,472,6,6,WALK 1475 | Graph,Adjacent,445,416,6,3,DOOR 1476 | Graph,Adjacent,449,450,4,4,WALK 1477 | Graph,Adjacent,449,477,4,4,WALK 1478 | Graph,Adjacent,449,421,4,4,WALK 1479 | Graph,Adjacent,449,478,4,4,WALK 1480 | Graph,Adjacent,449,422,4,4,WALK 1481 | Graph,Adjacent,450,451,4,4,WALK 1482 | Graph,Adjacent,450,449,4,4,WALK 1483 | Graph,Adjacent,450,478,4,4,WALK 1484 | Graph,Adjacent,450,422,4,4,WALK 1485 | Graph,Adjacent,450,423,4,4,WALK 1486 | Graph,Adjacent,450,477,4,4,WALK 1487 | Graph,Adjacent,450,421,4,4,WALK 1488 | Graph,Adjacent,451,452,4,4,WALK 1489 | Graph,Adjacent,451,450,4,4,WALK 1490 | Graph,Adjacent,451,423,4,4,WALK 1491 | Graph,Adjacent,451,424,4,4,WALK 1492 | Graph,Adjacent,451,478,4,4,WALK 1493 | Graph,Adjacent,451,422,4,4,WALK 1494 | Graph,Adjacent,452,451,4,4,WALK 1495 | Graph,Adjacent,452,424,4,4,WALK 1496 | Graph,Adjacent,452,425,4,4,WALK 1497 | Graph,Adjacent,452,423,4,4,WALK 1498 | Graph,Adjacent,457,485,5,5,WALK 1499 | Graph,Adjacent,457,486,5,5,WALK 1500 | Graph,Adjacent,457,430,5,5,WALK 1501 | Graph,Adjacent,459,460,5,5,WALK 1502 | Graph,Adjacent,459,487,5,5,WALK 1503 | Graph,Adjacent,459,431,5,5,WALK 1504 | Graph,Adjacent,459,488,5,5,WALK 1505 | Graph,Adjacent,459,432,5,5,WALK 1506 | Graph,Adjacent,459,486,5,5,WALK 1507 | Graph,Adjacent,459,430,5,5,WALK 1508 | Graph,Adjacent,460,461,5,5,WALK 1509 | Graph,Adjacent,460,459,5,5,WALK 1510 | Graph,Adjacent,460,488,5,5,WALK 1511 | Graph,Adjacent,460,432,5,5,WALK 1512 | Graph,Adjacent,460,489,5,5,WALK 1513 | Graph,Adjacent,460,433,5,5,WALK 1514 | Graph,Adjacent,460,487,5,5,WALK 1515 | Graph,Adjacent,460,431,5,5,WALK 1516 | Graph,Adjacent,461,462,5,5,WALK 1517 | Graph,Adjacent,461,460,5,5,WALK 1518 | Graph,Adjacent,461,489,5,5,WALK 1519 | Graph,Adjacent,461,433,5,5,WALK 1520 | Graph,Adjacent,461,490,5,5,WALK 1521 | Graph,Adjacent,461,434,5,5,WALK 1522 | Graph,Adjacent,461,488,5,5,WALK 1523 | Graph,Adjacent,461,432,5,5,WALK 1524 | Graph,Adjacent,462,463,5,5,WALK 1525 | Graph,Adjacent,462,461,5,5,WALK 1526 | Graph,Adjacent,462,490,5,5,WALK 1527 | Graph,Adjacent,462,434,5,5,WALK 1528 | Graph,Adjacent,462,491,5,5,WALK 1529 | Graph,Adjacent,462,435,5,5,WALK 1530 | Graph,Adjacent,462,489,5,5,WALK 1531 | Graph,Adjacent,462,433,5,5,WALK 1532 | Graph,Adjacent,463,464,5,5,WALK 1533 | Graph,Adjacent,463,462,5,5,WALK 1534 | Graph,Adjacent,463,491,5,5,WALK 1535 | Graph,Adjacent,463,435,5,5,WALK 1536 | Graph,Adjacent,463,492,5,5,WALK 1537 | Graph,Adjacent,463,436,5,5,WALK 1538 | Graph,Adjacent,463,490,5,5,WALK 1539 | Graph,Adjacent,463,434,5,5,WALK 1540 | Graph,Adjacent,464,465,5,5,WALK 1541 | Graph,Adjacent,464,463,5,5,WALK 1542 | Graph,Adjacent,464,492,5,5,WALK 1543 | Graph,Adjacent,464,436,5,5,WALK 1544 | Graph,Adjacent,464,493,5,5,WALK 1545 | Graph,Adjacent,464,437,5,5,WALK 1546 | Graph,Adjacent,464,491,5,5,WALK 1547 | Graph,Adjacent,464,435,5,5,WALK 1548 | Graph,Adjacent,465,466,5,5,WALK 1549 | Graph,Adjacent,465,464,5,5,WALK 1550 | Graph,Adjacent,465,493,5,5,WALK 1551 | Graph,Adjacent,465,437,5,5,WALK 1552 | Graph,Adjacent,465,494,5,5,WALK 1553 | Graph,Adjacent,465,438,5,5,WALK 1554 | Graph,Adjacent,465,492,5,5,WALK 1555 | Graph,Adjacent,465,436,5,5,WALK 1556 | Graph,Adjacent,466,467,5,5,WALK 1557 | Graph,Adjacent,466,465,5,5,WALK 1558 | Graph,Adjacent,466,494,5,5,WALK 1559 | Graph,Adjacent,466,438,5,5,WALK 1560 | Graph,Adjacent,466,495,5,5,WALK 1561 | Graph,Adjacent,466,439,5,5,WALK 1562 | Graph,Adjacent,466,493,5,5,WALK 1563 | Graph,Adjacent,466,437,5,5,WALK 1564 | Graph,Adjacent,467,468,5,5,WALK 1565 | Graph,Adjacent,467,466,5,5,WALK 1566 | Graph,Adjacent,467,495,5,5,WALK 1567 | Graph,Adjacent,467,439,5,5,WALK 1568 | Graph,Adjacent,467,496,5,5,WALK 1569 | Graph,Adjacent,467,440,5,5,WALK 1570 | Graph,Adjacent,467,494,5,5,WALK 1571 | Graph,Adjacent,467,438,5,5,WALK 1572 | Graph,Adjacent,468,469,5,5,WALK 1573 | Graph,Adjacent,468,467,5,5,WALK 1574 | Graph,Adjacent,468,496,5,5,WALK 1575 | Graph,Adjacent,468,440,5,5,WALK 1576 | Graph,Adjacent,468,497,5,5,WALK 1577 | Graph,Adjacent,468,441,5,5,WALK 1578 | Graph,Adjacent,468,495,5,5,WALK 1579 | Graph,Adjacent,468,439,5,5,WALK 1580 | Graph,Adjacent,469,470,5,5,WALK 1581 | Graph,Adjacent,469,468,5,5,WALK 1582 | Graph,Adjacent,469,497,5,5,WALK 1583 | Graph,Adjacent,469,441,5,5,WALK 1584 | Graph,Adjacent,469,496,5,5,WALK 1585 | Graph,Adjacent,469,440,5,5,WALK 1586 | Graph,Adjacent,470,471,5,6,ROOM 1587 | Graph,Adjacent,470,469,5,5,WALK 1588 | Graph,Adjacent,470,497,5,5,WALK 1589 | Graph,Adjacent,470,441,5,5,WALK 1590 | Graph,Adjacent,471,472,6,6,WALK 1591 | Graph,Adjacent,471,470,6,5,ROOM 1592 | Graph,Adjacent,471,500,6,6,WALK 1593 | Graph,Adjacent,471,444,6,6,WALK 1594 | Graph,Adjacent,472,473,6,6,WALK 1595 | Graph,Adjacent,472,471,6,6,WALK 1596 | Graph,Adjacent,472,500,6,6,WALK 1597 | Graph,Adjacent,472,444,6,6,WALK 1598 | Graph,Adjacent,472,501,6,6,WALK 1599 | Graph,Adjacent,472,445,6,6,WALK 1600 | Graph,Adjacent,473,472,6,6,WALK 1601 | Graph,Adjacent,473,501,6,6,WALK 1602 | Graph,Adjacent,473,445,6,6,WALK 1603 | Graph,Adjacent,473,500,6,6,WALK 1604 | Graph,Adjacent,473,444,6,6,WALK 1605 | Graph,Adjacent,477,478,4,4,WALK 1606 | Graph,Adjacent,477,505,4,4,WALK 1607 | Graph,Adjacent,477,449,4,4,WALK 1608 | Graph,Adjacent,477,506,4,4,WALK 1609 | Graph,Adjacent,477,450,4,4,WALK 1610 | Graph,Adjacent,478,477,4,4,WALK 1611 | Graph,Adjacent,478,506,4,4,WALK 1612 | Graph,Adjacent,478,450,4,4,WALK 1613 | Graph,Adjacent,478,451,4,4,WALK 1614 | Graph,Adjacent,478,505,4,4,WALK 1615 | Graph,Adjacent,478,449,4,4,WALK 1616 | Graph,Adjacent,485,486,5,5,WALK 1617 | Graph,Adjacent,485,513,5,5,WALK 1618 | Graph,Adjacent,485,457,5,5,WALK 1619 | Graph,Adjacent,486,487,5,5,WALK 1620 | Graph,Adjacent,486,485,5,5,WALK 1621 | Graph,Adjacent,486,459,5,5,WALK 1622 | Graph,Adjacent,486,513,5,5,WALK 1623 | Graph,Adjacent,486,457,5,5,WALK 1624 | Graph,Adjacent,487,488,5,5,WALK 1625 | Graph,Adjacent,487,486,5,5,WALK 1626 | Graph,Adjacent,487,459,5,5,WALK 1627 | Graph,Adjacent,487,460,5,5,WALK 1628 | Graph,Adjacent,488,489,5,5,WALK 1629 | Graph,Adjacent,488,487,5,5,WALK 1630 | Graph,Adjacent,488,460,5,5,WALK 1631 | Graph,Adjacent,488,461,5,5,WALK 1632 | Graph,Adjacent,488,459,5,5,WALK 1633 | Graph,Adjacent,489,490,5,5,WALK 1634 | Graph,Adjacent,489,488,5,5,WALK 1635 | Graph,Adjacent,489,461,5,5,WALK 1636 | Graph,Adjacent,489,462,5,5,WALK 1637 | Graph,Adjacent,489,460,5,5,WALK 1638 | Graph,Adjacent,490,491,5,5,WALK 1639 | Graph,Adjacent,490,489,5,5,WALK 1640 | Graph,Adjacent,490,462,5,5,WALK 1641 | Graph,Adjacent,490,463,5,5,WALK 1642 | Graph,Adjacent,490,461,5,5,WALK 1643 | Graph,Adjacent,491,492,5,5,WALK 1644 | Graph,Adjacent,491,490,5,5,WALK 1645 | Graph,Adjacent,491,463,5,5,WALK 1646 | Graph,Adjacent,491,464,5,5,WALK 1647 | Graph,Adjacent,491,462,5,5,WALK 1648 | Graph,Adjacent,492,493,5,5,WALK 1649 | Graph,Adjacent,492,491,5,5,WALK 1650 | Graph,Adjacent,492,464,5,5,WALK 1651 | Graph,Adjacent,492,521,5,5,WALK 1652 | Graph,Adjacent,492,465,5,5,WALK 1653 | Graph,Adjacent,492,463,5,5,WALK 1654 | Graph,Adjacent,493,494,5,5,WALK 1655 | Graph,Adjacent,493,492,5,5,WALK 1656 | Graph,Adjacent,493,521,5,5,WALK 1657 | Graph,Adjacent,493,465,5,5,WALK 1658 | Graph,Adjacent,493,466,5,5,WALK 1659 | Graph,Adjacent,493,464,5,5,WALK 1660 | Graph,Adjacent,494,495,5,5,WALK 1661 | Graph,Adjacent,494,493,5,5,WALK 1662 | Graph,Adjacent,494,466,5,5,WALK 1663 | Graph,Adjacent,494,467,5,5,WALK 1664 | Graph,Adjacent,494,521,5,5,WALK 1665 | Graph,Adjacent,494,465,5,5,WALK 1666 | Graph,Adjacent,495,496,5,5,WALK 1667 | Graph,Adjacent,495,494,5,5,WALK 1668 | Graph,Adjacent,495,467,5,5,WALK 1669 | Graph,Adjacent,495,468,5,5,WALK 1670 | Graph,Adjacent,495,466,5,5,WALK 1671 | Graph,Adjacent,496,497,5,5,WALK 1672 | Graph,Adjacent,496,495,5,5,WALK 1673 | Graph,Adjacent,496,468,5,5,WALK 1674 | Graph,Adjacent,496,469,5,5,WALK 1675 | Graph,Adjacent,496,467,5,5,WALK 1676 | Graph,Adjacent,497,496,5,5,WALK 1677 | Graph,Adjacent,497,469,5,5,WALK 1678 | Graph,Adjacent,497,470,5,5,WALK 1679 | Graph,Adjacent,497,468,5,5,WALK 1680 | Graph,Adjacent,500,501,6,6,WALK 1681 | Graph,Adjacent,500,528,6,6,WALK 1682 | Graph,Adjacent,500,472,6,6,WALK 1683 | Graph,Adjacent,500,529,6,6,WALK 1684 | Graph,Adjacent,500,473,6,6,WALK 1685 | Graph,Adjacent,500,471,6,6,WALK 1686 | Graph,Adjacent,501,500,6,6,WALK 1687 | Graph,Adjacent,501,529,6,6,WALK 1688 | Graph,Adjacent,501,473,6,6,WALK 1689 | Graph,Adjacent,501,528,6,6,WALK 1690 | Graph,Adjacent,501,472,6,6,WALK 1691 | Graph,Adjacent,505,506,4,4,WALK 1692 | Graph,Adjacent,505,533,4,4,WALK 1693 | Graph,Adjacent,505,477,4,4,WALK 1694 | Graph,Adjacent,505,534,4,4,WALK 1695 | Graph,Adjacent,505,478,4,4,WALK 1696 | Graph,Adjacent,506,505,4,4,WALK 1697 | Graph,Adjacent,506,534,4,4,WALK 1698 | Graph,Adjacent,506,478,4,4,WALK 1699 | Graph,Adjacent,506,533,4,4,WALK 1700 | Graph,Adjacent,506,477,4,4,WALK 1701 | Graph,Adjacent,513,541,5,6,ROOM 1702 | Graph,Adjacent,513,485,5,5,WALK 1703 | Graph,Adjacent,513,486,5,5,WALK 1704 | Graph,Adjacent,521,549,5,6,ROOM 1705 | Graph,Adjacent,521,493,5,5,WALK 1706 | Graph,Adjacent,521,494,5,5,WALK 1707 | Graph,Adjacent,521,492,5,5,WALK 1708 | Graph,Adjacent,528,529,6,6,WALK 1709 | Graph,Adjacent,528,556,6,6,WALK 1710 | Graph,Adjacent,528,500,6,6,WALK 1711 | Graph,Adjacent,528,557,6,6,WALK 1712 | Graph,Adjacent,528,501,6,6,WALK 1713 | Graph,Adjacent,529,528,6,6,WALK 1714 | Graph,Adjacent,529,557,6,6,WALK 1715 | Graph,Adjacent,529,501,6,6,WALK 1716 | Graph,Adjacent,529,556,6,6,WALK 1717 | Graph,Adjacent,529,500,6,6,WALK 1718 | Graph,Adjacent,533,534,4,4,WALK 1719 | Graph,Adjacent,533,561,4,4,WALK 1720 | Graph,Adjacent,533,505,4,4,WALK 1721 | Graph,Adjacent,533,562,4,4,WALK 1722 | Graph,Adjacent,533,506,4,4,WALK 1723 | Graph,Adjacent,534,533,4,4,WALK 1724 | Graph,Adjacent,534,562,4,4,WALK 1725 | Graph,Adjacent,534,506,4,4,WALK 1726 | Graph,Adjacent,534,563,4,4,WALK 1727 | Graph,Adjacent,534,561,4,4,WALK 1728 | Graph,Adjacent,534,505,4,4,WALK 1729 | Graph,Adjacent,541,569,6,6,WALK 1730 | Graph,Adjacent,541,513,6,5,ROOM 1731 | Graph,Adjacent,541,570,6,6,WALK 1732 | Graph,Adjacent,541,568,6,6,WALK 1733 | Graph,Adjacent,549,577,6,6,WALK 1734 | Graph,Adjacent,549,521,6,5,ROOM 1735 | Graph,Adjacent,549,578,6,6,WALK 1736 | Graph,Adjacent,549,576,6,6,WALK 1737 | Graph,Adjacent,556,557,6,6,WALK 1738 | Graph,Adjacent,556,584,6,6,WALK 1739 | Graph,Adjacent,556,528,6,6,WALK 1740 | Graph,Adjacent,556,585,6,6,WALK 1741 | Graph,Adjacent,556,529,6,6,WALK 1742 | Graph,Adjacent,556,583,6,6,WALK 1743 | Graph,Adjacent,557,556,6,6,WALK 1744 | Graph,Adjacent,557,585,6,6,WALK 1745 | Graph,Adjacent,557,529,6,6,WALK 1746 | Graph,Adjacent,557,584,6,6,WALK 1747 | Graph,Adjacent,557,528,6,6,WALK 1748 | Graph,Adjacent,561,562,4,4,WALK 1749 | Graph,Adjacent,561,589,4,4,WALK 1750 | Graph,Adjacent,561,533,4,4,WALK 1751 | Graph,Adjacent,561,590,4,4,WALK 1752 | Graph,Adjacent,561,534,4,4,WALK 1753 | Graph,Adjacent,562,563,4,4,WALK 1754 | Graph,Adjacent,562,561,4,4,WALK 1755 | Graph,Adjacent,562,590,4,4,WALK 1756 | Graph,Adjacent,562,534,4,4,WALK 1757 | Graph,Adjacent,562,591,4,4,WALK 1758 | Graph,Adjacent,562,589,4,4,WALK 1759 | Graph,Adjacent,562,533,4,4,WALK 1760 | Graph,Adjacent,563,564,4,4,WALK 1761 | Graph,Adjacent,563,562,4,4,WALK 1762 | Graph,Adjacent,563,591,4,4,WALK 1763 | Graph,Adjacent,563,592,4,4,WALK 1764 | Graph,Adjacent,563,590,4,4,WALK 1765 | Graph,Adjacent,563,534,4,4,WALK 1766 | Graph,Adjacent,564,565,4,4,WALK 1767 | Graph,Adjacent,564,563,4,4,WALK 1768 | Graph,Adjacent,564,592,4,4,WALK 1769 | Graph,Adjacent,564,593,4,4,WALK 1770 | Graph,Adjacent,564,591,4,4,WALK 1771 | Graph,Adjacent,565,566,4,4,WALK 1772 | Graph,Adjacent,565,564,4,4,WALK 1773 | Graph,Adjacent,565,593,4,4,WALK 1774 | Graph,Adjacent,565,594,4,4,WALK 1775 | Graph,Adjacent,565,592,4,4,WALK 1776 | Graph,Adjacent,566,567,4,4,WALK 1777 | Graph,Adjacent,566,565,4,4,WALK 1778 | Graph,Adjacent,566,594,4,4,WALK 1779 | Graph,Adjacent,566,595,4,4,WALK 1780 | Graph,Adjacent,566,593,4,4,WALK 1781 | Graph,Adjacent,567,568,4,6,DOOR 1782 | Graph,Adjacent,567,566,4,4,WALK 1783 | Graph,Adjacent,567,595,4,4,WALK 1784 | Graph,Adjacent,567,596,4,6,DOOR 1785 | Graph,Adjacent,567,594,4,4,WALK 1786 | Graph,Adjacent,568,569,6,6,WALK 1787 | Graph,Adjacent,568,567,6,4,DOOR 1788 | Graph,Adjacent,568,596,6,6,WALK 1789 | Graph,Adjacent,568,597,6,6,WALK 1790 | Graph,Adjacent,568,541,6,6,WALK 1791 | Graph,Adjacent,568,595,6,4,DOOR 1792 | Graph,Adjacent,569,570,6,6,WALK 1793 | Graph,Adjacent,569,568,6,6,WALK 1794 | Graph,Adjacent,569,597,6,6,WALK 1795 | Graph,Adjacent,569,541,6,6,WALK 1796 | Graph,Adjacent,569,598,6,6,WALK 1797 | Graph,Adjacent,569,596,6,6,WALK 1798 | Graph,Adjacent,570,571,6,6,WALK 1799 | Graph,Adjacent,570,569,6,6,WALK 1800 | Graph,Adjacent,570,598,6,6,WALK 1801 | Graph,Adjacent,570,599,6,6,WALK 1802 | Graph,Adjacent,570,597,6,6,WALK 1803 | Graph,Adjacent,570,541,6,6,WALK 1804 | Graph,Adjacent,571,572,6,6,WALK 1805 | Graph,Adjacent,571,570,6,6,WALK 1806 | Graph,Adjacent,571,599,6,6,WALK 1807 | Graph,Adjacent,571,600,6,6,WALK 1808 | Graph,Adjacent,571,598,6,6,WALK 1809 | Graph,Adjacent,572,573,6,6,WALK 1810 | Graph,Adjacent,572,571,6,6,WALK 1811 | Graph,Adjacent,572,600,6,6,WALK 1812 | Graph,Adjacent,572,601,6,6,WALK 1813 | Graph,Adjacent,572,599,6,6,WALK 1814 | Graph,Adjacent,573,574,6,6,WALK 1815 | Graph,Adjacent,573,572,6,6,WALK 1816 | Graph,Adjacent,573,601,6,6,WALK 1817 | Graph,Adjacent,573,602,6,6,WALK 1818 | Graph,Adjacent,573,600,6,6,WALK 1819 | Graph,Adjacent,574,575,6,6,WALK 1820 | Graph,Adjacent,574,573,6,6,WALK 1821 | Graph,Adjacent,574,602,6,6,WALK 1822 | Graph,Adjacent,574,603,6,6,WALK 1823 | Graph,Adjacent,574,601,6,6,WALK 1824 | Graph,Adjacent,575,576,6,6,WALK 1825 | Graph,Adjacent,575,574,6,6,WALK 1826 | Graph,Adjacent,575,603,6,6,WALK 1827 | Graph,Adjacent,575,604,6,6,WALK 1828 | Graph,Adjacent,575,602,6,6,WALK 1829 | Graph,Adjacent,576,577,6,6,WALK 1830 | Graph,Adjacent,576,575,6,6,WALK 1831 | Graph,Adjacent,576,604,6,6,WALK 1832 | Graph,Adjacent,576,605,6,6,WALK 1833 | Graph,Adjacent,576,549,6,6,WALK 1834 | Graph,Adjacent,576,603,6,6,WALK 1835 | Graph,Adjacent,577,578,6,6,WALK 1836 | Graph,Adjacent,577,576,6,6,WALK 1837 | Graph,Adjacent,577,605,6,6,WALK 1838 | Graph,Adjacent,577,549,6,6,WALK 1839 | Graph,Adjacent,577,606,6,6,WALK 1840 | Graph,Adjacent,577,604,6,6,WALK 1841 | Graph,Adjacent,578,579,6,6,WALK 1842 | Graph,Adjacent,578,577,6,6,WALK 1843 | Graph,Adjacent,578,606,6,6,WALK 1844 | Graph,Adjacent,578,607,6,6,WALK 1845 | Graph,Adjacent,578,605,6,6,WALK 1846 | Graph,Adjacent,578,549,6,6,WALK 1847 | Graph,Adjacent,579,580,6,6,WALK 1848 | Graph,Adjacent,579,578,6,6,WALK 1849 | Graph,Adjacent,579,607,6,6,WALK 1850 | Graph,Adjacent,579,608,6,6,WALK 1851 | Graph,Adjacent,579,606,6,6,WALK 1852 | Graph,Adjacent,580,581,6,6,WALK 1853 | Graph,Adjacent,580,579,6,6,WALK 1854 | Graph,Adjacent,580,608,6,6,WALK 1855 | Graph,Adjacent,580,609,6,6,WALK 1856 | Graph,Adjacent,580,607,6,6,WALK 1857 | Graph,Adjacent,581,582,6,6,WALK 1858 | Graph,Adjacent,581,580,6,6,WALK 1859 | Graph,Adjacent,581,609,6,6,WALK 1860 | Graph,Adjacent,581,610,6,6,WALK 1861 | Graph,Adjacent,581,608,6,6,WALK 1862 | Graph,Adjacent,582,583,6,6,WALK 1863 | Graph,Adjacent,582,581,6,6,WALK 1864 | Graph,Adjacent,582,610,6,6,WALK 1865 | Graph,Adjacent,582,611,6,6,WALK 1866 | Graph,Adjacent,582,609,6,6,WALK 1867 | Graph,Adjacent,583,584,6,6,WALK 1868 | Graph,Adjacent,583,582,6,6,WALK 1869 | Graph,Adjacent,583,611,6,6,WALK 1870 | Graph,Adjacent,583,612,6,6,WALK 1871 | Graph,Adjacent,583,556,6,6,WALK 1872 | Graph,Adjacent,583,610,6,6,WALK 1873 | Graph,Adjacent,584,585,6,6,WALK 1874 | Graph,Adjacent,584,583,6,6,WALK 1875 | Graph,Adjacent,584,612,6,6,WALK 1876 | Graph,Adjacent,584,556,6,6,WALK 1877 | Graph,Adjacent,584,613,6,6,WALK 1878 | Graph,Adjacent,584,557,6,6,WALK 1879 | Graph,Adjacent,584,611,6,6,WALK 1880 | Graph,Adjacent,585,584,6,6,WALK 1881 | Graph,Adjacent,585,613,6,6,WALK 1882 | Graph,Adjacent,585,557,6,6,WALK 1883 | Graph,Adjacent,585,612,6,6,WALK 1884 | Graph,Adjacent,585,556,6,6,WALK 1885 | Graph,Adjacent,589,590,4,4,WALK 1886 | Graph,Adjacent,589,561,4,4,WALK 1887 | Graph,Adjacent,589,562,4,4,WALK 1888 | Graph,Adjacent,590,591,4,4,WALK 1889 | Graph,Adjacent,590,589,4,4,WALK 1890 | Graph,Adjacent,590,562,4,4,WALK 1891 | Graph,Adjacent,590,563,4,4,WALK 1892 | Graph,Adjacent,590,561,4,4,WALK 1893 | Graph,Adjacent,591,592,4,4,WALK 1894 | Graph,Adjacent,591,590,4,4,WALK 1895 | Graph,Adjacent,591,563,4,4,WALK 1896 | Graph,Adjacent,591,564,4,4,WALK 1897 | Graph,Adjacent,591,562,4,4,WALK 1898 | Graph,Adjacent,592,593,4,4,WALK 1899 | Graph,Adjacent,592,591,4,4,WALK 1900 | Graph,Adjacent,592,564,4,4,WALK 1901 | Graph,Adjacent,592,565,4,4,WALK 1902 | Graph,Adjacent,592,563,4,4,WALK 1903 | Graph,Adjacent,593,594,4,4,WALK 1904 | Graph,Adjacent,593,592,4,4,WALK 1905 | Graph,Adjacent,593,565,4,4,WALK 1906 | Graph,Adjacent,593,566,4,4,WALK 1907 | Graph,Adjacent,593,564,4,4,WALK 1908 | Graph,Adjacent,594,595,4,4,WALK 1909 | Graph,Adjacent,594,593,4,4,WALK 1910 | Graph,Adjacent,594,566,4,4,WALK 1911 | Graph,Adjacent,594,567,4,4,WALK 1912 | Graph,Adjacent,594,565,4,4,WALK 1913 | Graph,Adjacent,595,596,4,6,DOOR 1914 | Graph,Adjacent,595,594,4,4,WALK 1915 | Graph,Adjacent,595,567,4,4,WALK 1916 | Graph,Adjacent,595,568,4,6,DOOR 1917 | Graph,Adjacent,595,566,4,4,WALK 1918 | Graph,Adjacent,596,597,6,6,WALK 1919 | Graph,Adjacent,596,595,6,4,DOOR 1920 | Graph,Adjacent,596,568,6,6,WALK 1921 | Graph,Adjacent,596,569,6,6,WALK 1922 | Graph,Adjacent,596,567,6,4,DOOR 1923 | Graph,Adjacent,597,598,6,6,WALK 1924 | Graph,Adjacent,597,596,6,6,WALK 1925 | Graph,Adjacent,597,569,6,6,WALK 1926 | Graph,Adjacent,597,570,6,6,WALK 1927 | Graph,Adjacent,597,568,6,6,WALK 1928 | Graph,Adjacent,598,599,6,6,WALK 1929 | Graph,Adjacent,598,597,6,6,WALK 1930 | Graph,Adjacent,598,570,6,6,WALK 1931 | Graph,Adjacent,598,571,6,6,WALK 1932 | Graph,Adjacent,598,569,6,6,WALK 1933 | Graph,Adjacent,599,600,6,6,WALK 1934 | Graph,Adjacent,599,598,6,6,WALK 1935 | Graph,Adjacent,599,571,6,6,WALK 1936 | Graph,Adjacent,599,572,6,6,WALK 1937 | Graph,Adjacent,599,570,6,6,WALK 1938 | Graph,Adjacent,600,601,6,6,WALK 1939 | Graph,Adjacent,600,599,6,6,WALK 1940 | Graph,Adjacent,600,572,6,6,WALK 1941 | Graph,Adjacent,600,573,6,6,WALK 1942 | Graph,Adjacent,600,571,6,6,WALK 1943 | Graph,Adjacent,601,602,6,6,WALK 1944 | Graph,Adjacent,601,600,6,6,WALK 1945 | Graph,Adjacent,601,573,6,6,WALK 1946 | Graph,Adjacent,601,574,6,6,WALK 1947 | Graph,Adjacent,601,572,6,6,WALK 1948 | Graph,Adjacent,602,603,6,6,WALK 1949 | Graph,Adjacent,602,601,6,6,WALK 1950 | Graph,Adjacent,602,574,6,6,WALK 1951 | Graph,Adjacent,602,575,6,6,WALK 1952 | Graph,Adjacent,602,573,6,6,WALK 1953 | Graph,Adjacent,603,604,6,6,WALK 1954 | Graph,Adjacent,603,602,6,6,WALK 1955 | Graph,Adjacent,603,575,6,6,WALK 1956 | Graph,Adjacent,603,576,6,6,WALK 1957 | Graph,Adjacent,603,574,6,6,WALK 1958 | Graph,Adjacent,604,605,6,6,WALK 1959 | Graph,Adjacent,604,603,6,6,WALK 1960 | Graph,Adjacent,604,576,6,6,WALK 1961 | Graph,Adjacent,604,577,6,6,WALK 1962 | Graph,Adjacent,604,575,6,6,WALK 1963 | Graph,Adjacent,605,606,6,6,WALK 1964 | Graph,Adjacent,605,604,6,6,WALK 1965 | Graph,Adjacent,605,577,6,6,WALK 1966 | Graph,Adjacent,605,578,6,6,WALK 1967 | Graph,Adjacent,605,576,6,6,WALK 1968 | Graph,Adjacent,606,607,6,6,WALK 1969 | Graph,Adjacent,606,605,6,6,WALK 1970 | Graph,Adjacent,606,578,6,6,WALK 1971 | Graph,Adjacent,606,579,6,6,WALK 1972 | Graph,Adjacent,606,577,6,6,WALK 1973 | Graph,Adjacent,607,608,6,6,WALK 1974 | Graph,Adjacent,607,606,6,6,WALK 1975 | Graph,Adjacent,607,579,6,6,WALK 1976 | Graph,Adjacent,607,580,6,6,WALK 1977 | Graph,Adjacent,607,578,6,6,WALK 1978 | Graph,Adjacent,608,609,6,6,WALK 1979 | Graph,Adjacent,608,607,6,6,WALK 1980 | Graph,Adjacent,608,580,6,6,WALK 1981 | Graph,Adjacent,608,581,6,6,WALK 1982 | Graph,Adjacent,608,579,6,6,WALK 1983 | Graph,Adjacent,609,610,6,6,WALK 1984 | Graph,Adjacent,609,608,6,6,WALK 1985 | Graph,Adjacent,609,581,6,6,WALK 1986 | Graph,Adjacent,609,582,6,6,WALK 1987 | Graph,Adjacent,609,580,6,6,WALK 1988 | Graph,Adjacent,610,611,6,6,WALK 1989 | Graph,Adjacent,610,609,6,6,WALK 1990 | Graph,Adjacent,610,582,6,6,WALK 1991 | Graph,Adjacent,610,583,6,6,WALK 1992 | Graph,Adjacent,610,581,6,6,WALK 1993 | Graph,Adjacent,611,612,6,6,WALK 1994 | Graph,Adjacent,611,610,6,6,WALK 1995 | Graph,Adjacent,611,583,6,6,WALK 1996 | Graph,Adjacent,611,584,6,6,WALK 1997 | Graph,Adjacent,611,582,6,6,WALK 1998 | Graph,Adjacent,612,613,6,6,WALK 1999 | Graph,Adjacent,612,611,6,6,WALK 2000 | Graph,Adjacent,612,584,6,6,WALK 2001 | Graph,Adjacent,612,585,6,6,WALK 2002 | Graph,Adjacent,612,583,6,6,WALK 2003 | Graph,Adjacent,613,612,6,6,WALK 2004 | Graph,Adjacent,613,585,6,6,WALK 2005 | Graph,Adjacent,613,584,6,6,WALK 2006 | Room,Indices,0,29,30,31,32,33,34,35,36,57,58 2007 | Room,Indices,0,59,60,61,62,63,64,85,86,87,90 2008 | Room,Indices,0,91,92,113,114,118,119,120,121,141,142 2009 | Room,Indices,0,145,146,147,148,149,169,170,171,172,173 2010 | Room,Indices,0,174,175,176,197,198,199,200,201,202,203 2011 | Room,Indices,0,204,225,226,227,228,229,230,231,232,257 2012 | Room,Indices,0,258 2013 | Room,Indices,1,40,41,42,43,44,45,67,68,69,70 2014 | Room,Indices,1,71,72,73,74,98,99,122,123,124,125 2015 | Room,Indices,1,126,127,128,129,130,131,150,151,152,153 2016 | Room,Indices,1,154,155,156,157,158,159,179,180,181,182 2017 | Room,Indices,1,183,184,185,186,207,208,209,212,213,214 2018 | Room,Indices,1,236,237,240,241,264,265,268,269,292,293 2019 | Room,Indices,1,294,295,296,297,320,321,322,323,324,325 2020 | Room,Indices,1,347,348,349,350,351,352,353,354,375,376 2021 | Room,Indices,1,377,378,379,380,381,382 2022 | Room,Indices,2,77,78,79,80,81,105,106,107,108,109 2023 | Room,Indices,2,110,132,133,134,135,136,137,138,160,161 2024 | Room,Indices,2,162,163,164,165,166,189,190,191,192,193 2025 | Room,Indices,2,194,220,221 2026 | Room,Indices,3,248,249,276,277,303,304,305,306,331,332 2027 | Room,Indices,3,333,334,359,360,361,362,388,389,416,417 2028 | Room,Indices,4,285,286,313,314,341,342,365,366,367,368 2029 | Room,Indices,4,369,370,393,394,395,396,397,398,421,422 2030 | Room,Indices,4,423,424,425,449,450,451,452,477,478,505 2031 | Room,Indices,4,506,533,534,561,562,563,564,565,566,567 2032 | Room,Indices,4,589,590,591,592,593,594,595 2033 | Room,Indices,5,405,406,407,408,430,431,432,433,434,435 2034 | Room,Indices,5,436,437,438,439,440,441,457,458,459,460 2035 | Room,Indices,5,461,462,463,464,465,466,467,468,469,470 2036 | Room,Indices,5,485,486,487,488,489,490,491,492,493,494 2037 | Room,Indices,5,495,496,497,513,521 2038 | Room,Indices,6,444,445,471,472,473,500,501,528,529,541 2039 | Room,Indices,6,549,556,557,568,569,570,571,572,573,574 2040 | Room,Indices,6,575,576,577,578,579,580,581,582,583,584 2041 | Room,Indices,6,585,596,597,598,599,600,601,602,603,604 2042 | Room,Indices,6,605,606,607,608,609,610,611,612,613 2043 | Object,Define,1,Activators 2044 | Object,Indices,1,176 2045 | Object,Binding,1,14 2046 | Object,Define,2,Activators 2047 | Object,Indices,2,179 2048 | Object,Binding,2,14 2049 | Object,Define,3,Activators 2050 | Object,Indices,3,186 2051 | Object,Binding,3,15 2052 | Object,Define,4,Activators 2053 | Object,Indices,4,189 2054 | Object,Binding,4,15 2055 | Object,Define,5,Activators 2056 | Object,Indices,5,220,249 2057 | Object,Binding,5,16 2058 | Object,Define,6,Activators 2059 | Object,Indices,6,228 2060 | Object,Binding,6,17 2061 | Object,Define,7,Activators 2062 | Object,Indices,7,314 2063 | Object,Binding,7,17 2064 | Object,Define,8,Activators 2065 | Object,Indices,8,417,444 2066 | Object,Binding,8,18 2067 | Object,Define,9,Activators 2068 | Object,Indices,9,594 2069 | Object,Binding,9,19 2070 | Object,Define,10,Activators 2071 | Object,Indices,10,597 2072 | Object,Binding,10,19 2073 | Object,Define,11,Ammo 2074 | Object,Indices,11,109 2075 | Object,Binding,11 2076 | Object,Define,12,Ammo 2077 | Object,Indices,12,394 2078 | Object,Binding,12 2079 | Object,Define,13,Ammo 2080 | Object,Indices,13,458 2081 | Object,Binding,13 2082 | Object,Define,14,Doors 2083 | Object,Indices,14,121,122,149,150 2084 | Object,Binding,14,1,2 2085 | Object,Define,15,Doors 2086 | Object,Indices,15,131,132,159,160 2087 | Object,Binding,15,3,4 2088 | Object,Define,16,Doors 2089 | Object,Indices,16,220,221,248,249 2090 | Object,Binding,16,5 2091 | Object,Define,17,Doors 2092 | Object,Indices,17,257,258,285,286 2093 | Object,Binding,17,6,7 2094 | Object,Define,18,Doors 2095 | Object,Indices,18,416,417,444,445 2096 | Object,Binding,18,8 2097 | Object,Define,19,Doors 2098 | Object,Indices,19,567,568,595,596 2099 | Object,Binding,19,9,10 2100 | Object,Define,20,Healing 2101 | Object,Indices,20,58 2102 | Object,Binding,20 2103 | Object,Define,21,Healing 2104 | Object,Indices,21,333 2105 | Object,Binding,21 2106 | Object,Define,22,Weapons 2107 | Object,Indices,22,40 2108 | Object,Binding,22 2109 | Object,Define,23,Weapons 2110 | Object,Indices,23,469 2111 | Object,Binding,23 2112 | -------------------------------------------------------------------------------- /Portals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Portals.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Map-Tiler 2 | ========= 3 | 4 | A Python script to take a series of images/layers and turn them into a Tiled file along with a tileset. 5 | 6 | I was working on a project and created a map with multiple layers (floor, walls, doors, objects, etc.) in a vector art program. 7 | When I wanted to import this into a tilemap program (Pyxel Editor, Tiled), I found that while they could both chop up the images 8 | into tiles (and PyxelEdit could even merge duplicates), it DID NOT AUTOMATICALLY CREATE LAYERS FOR THE IMAGES. So I had to 9 | create the art, then manually create the orientation of the tiles in the second tool. That seems a bit...inefficient. 10 | 11 | Currently, it will do the following: 12 | 13 | 1. Import a series of images by a (glob) pattern or a specific set of file names. 14 | 15 | 2. Verify all the images are the same size. 16 | 17 | 3. Verify they are an even multiple of the tileWidth and tileHeight (once the cropping is done). 18 | 19 | 4. Chop up the images into a tileset of tileWidth x tileHeight size, removing duplicates. Duplicates are 20 | checked based on the eight cardinal "flips" that Tiled uses (flipX, flipY, flipDiagonal). 21 | 22 | 5. Generate an output tileset (named as an input). 23 | 24 | 6. Generate an output Tiled file (named as an input). 25 | 26 | See the notes on check-ins to see future work and plans. 27 | -------------------------------------------------------------------------------- /Screen Shot 2015-01-05 at 6.42.20 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Screen Shot 2015-01-05 at 6.42.20 PM.png -------------------------------------------------------------------------------- /Transformers Movie.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Transformers Movie.ttf -------------------------------------------------------------------------------- /Transformers font.txt: -------------------------------------------------------------------------------- 1 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 2 | x x 3 | x TRANSFORMERS MOVIE FONT x 4 | x from film direct by Michael Bay x x 5 | x x 6 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 7 | 8 | 9 | Font name: TRANSFORMERS movie - Latin Decorative font (versione 1.0) 10 | Tipo: True Type regular 11 | Type designer: P. A. Vannucci (Font Lab Studio 5.0) 12 | sito internet: www.alphabeType.it 13 | www.paolo.arfelli.mclink.net/antares.htm 14 | e.mail: vannucci@alphabetype.it 15 | 16 | FREEWARE FONT for personal & professional use, by Alphabet & Type (r) (c)2009 17 | ----------------------------------------------------------------------------- 18 | 19 | Dall' eterno conflitto tra gli Autobots e Decepticons,nuove forze aliene contrastano la Terra. 20 | Il font originale del film. 21 | 22 | From eternal conflict between Autobots and Decepticons, new aliens force opposed the Earth. 23 | The original font of the film. -------------------------------------------------------------------------------- /Walls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Walls.png -------------------------------------------------------------------------------- /Weapons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/Weapons.png -------------------------------------------------------------------------------- /docopt.py: -------------------------------------------------------------------------------- 1 | """Pythonic command-line interface parser that will make you smile. 2 | * http://docopt.org 3 | * Repository and issue-tracker: https://github.com/docopt/docopt 4 | * Licensed under terms of MIT license (see below) 5 | * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com 6 | 7 | Copyright (c) 2012 Vladimir Keleshev, 8 | 9 | Permission is hereby granted, free of charge, to any person 10 | obtaining a copy of this software and associated 11 | documentation files (the "Software"), to deal in the Software 12 | without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, 14 | and/or sell copies of the Software, and to permit persons to 15 | whom the Software is furnished to do so, subject to the 16 | following conditions: 17 | 18 | The above copyright notice and this permission notice shall 19 | be included in all copies or substantial portions of the 20 | Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 23 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 24 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 25 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 26 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 28 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 29 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | """ 31 | import sys 32 | import re 33 | 34 | 35 | __all__ = ['docopt'] 36 | __version__ = '0.6.1' 37 | 38 | 39 | class DocoptLanguageError(Exception): 40 | """Error in construction of usage-message by developer.""" 41 | 42 | 43 | class DocoptExit(SystemExit): 44 | """Exit in case user invoked program with incorrect arguments.""" 45 | 46 | usage = '' 47 | 48 | def __init__(self, message=''): 49 | SystemExit.__init__(self, (message + '\n' + self.usage).strip()) 50 | 51 | 52 | class Pattern(object): 53 | def __eq__(self, other): 54 | return repr(self) == repr(other) 55 | 56 | def __hash__(self): 57 | return hash(repr(self)) 58 | 59 | def fix(self): 60 | self.fix_identities() 61 | self.fix_repeating_arguments() 62 | return self 63 | 64 | def fix_identities(self, uniq=None): 65 | """Make pattern-tree tips point to same object if they are equal.""" 66 | if not hasattr(self, 'children'): 67 | return self 68 | uniq = list(set(self.flat())) if uniq is None else uniq 69 | for i, child in enumerate(self.children): 70 | if not hasattr(child, 'children'): 71 | assert child in uniq 72 | self.children[i] = uniq[uniq.index(child)] 73 | else: 74 | child.fix_identities(uniq) 75 | 76 | def fix_repeating_arguments(self): 77 | """Fix elements that should accumulate/increment values.""" 78 | either = [list(child.children) for child in transform(self).children] 79 | for case in either: 80 | for e in [child for child in case if case.count(child) > 1]: 81 | if type(e) is Argument or type(e) is Option and e.argcount: 82 | if e.value is None: 83 | e.value = [] 84 | elif type(e.value) is not list: 85 | e.value = e.value.split() 86 | if type(e) is Command or type(e) is Option and e.argcount == 0: 87 | e.value = 0 88 | return self 89 | 90 | 91 | def transform(pattern): 92 | """Expand pattern into an (almost) equivalent one, but with single Either. 93 | Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) 94 | Quirks: [-a] => (-a), (-a...) => (-a -a) 95 | """ 96 | result = [] 97 | groups = [[pattern]] 98 | while groups: 99 | children = groups.pop(0) 100 | parents = [Required, Optional, OptionsShortcut, Either, OneOrMore] 101 | if any(t in map(type, children) for t in parents): 102 | child = [c for c in children if type(c) in parents][0] 103 | children.remove(child) 104 | if type(child) is Either: 105 | for c in child.children: 106 | groups.append([c] + children) 107 | elif type(child) is OneOrMore: 108 | groups.append(child.children * 2 + children) 109 | else: 110 | groups.append(child.children + children) 111 | else: 112 | result.append(children) 113 | return Either(*[Required(*e) for e in result]) 114 | 115 | 116 | class LeafPattern(Pattern): 117 | """Leaf/terminal node of a pattern tree.""" 118 | 119 | def __init__(self, name, value=None): 120 | self.name, self.value = name, value 121 | 122 | def __repr__(self): 123 | return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) 124 | 125 | def flat(self, *types): 126 | return [self] if not types or type(self) in types else [] 127 | 128 | def match(self, left, collected=None): 129 | collected = [] if collected is None else collected 130 | pos, match = self.single_match(left) 131 | if match is None: 132 | return False, left, collected 133 | left_ = left[:pos] + left[pos + 1:] 134 | same_name = [a for a in collected if a.name == self.name] 135 | if type(self.value) in (int, list): 136 | if type(self.value) is int: 137 | increment = 1 138 | else: 139 | increment = ([match.value] if type(match.value) is str 140 | else match.value) 141 | if not same_name: 142 | match.value = increment 143 | return True, left_, collected + [match] 144 | same_name[0].value += increment 145 | return True, left_, collected 146 | return True, left_, collected + [match] 147 | 148 | 149 | class BranchPattern(Pattern): 150 | """Branch/inner node of a pattern tree.""" 151 | 152 | def __init__(self, *children): 153 | self.children = list(children) 154 | 155 | def __repr__(self): 156 | return '%s(%s)' % (self.__class__.__name__, 157 | ', '.join(repr(a) for a in self.children)) 158 | 159 | def flat(self, *types): 160 | if type(self) in types: 161 | return [self] 162 | return sum([child.flat(*types) for child in self.children], []) 163 | 164 | 165 | class Argument(LeafPattern): 166 | def single_match(self, left): 167 | for n, pattern in enumerate(left): 168 | if type(pattern) is Argument: 169 | return n, Argument(self.name, pattern.value) 170 | return None, None 171 | 172 | @classmethod 173 | def parse(class_, source): 174 | name = re.findall('(<\S*?>)', source)[0] 175 | value = re.findall('\[default: (.*)\]', source, flags=re.I) 176 | return class_(name, value[0] if value else None) 177 | 178 | 179 | class Command(Argument): 180 | def __init__(self, name, value=False): 181 | self.name, self.value = name, value 182 | 183 | def single_match(self, left): 184 | for n, pattern in enumerate(left): 185 | if type(pattern) is Argument: 186 | if pattern.value == self.name: 187 | return n, Command(self.name, True) 188 | else: 189 | break 190 | return None, None 191 | 192 | 193 | class Option(LeafPattern): 194 | def __init__(self, short=None, long=None, argcount=0, value=False): 195 | assert argcount in (0, 1) 196 | self.short, self.long, self.argcount = short, long, argcount 197 | self.value = None if value is False and argcount else value 198 | 199 | @classmethod 200 | def parse(class_, option_description): 201 | short, long, argcount, value = None, None, 0, False 202 | options, _, description = option_description.strip().partition(' ') 203 | options = options.replace(',', ' ').replace('=', ' ') 204 | for s in options.split(): 205 | if s.startswith('--'): 206 | long = s 207 | elif s.startswith('-'): 208 | short = s 209 | else: 210 | argcount = 1 211 | if argcount: 212 | matched = re.findall('\[default: (.*)\]', description, flags=re.I) 213 | value = matched[0] if matched else None 214 | return class_(short, long, argcount, value) 215 | 216 | def single_match(self, left): 217 | for n, pattern in enumerate(left): 218 | if self.name == pattern.name: 219 | return n, pattern 220 | return None, None 221 | 222 | @property 223 | def name(self): 224 | return self.long or self.short 225 | 226 | def __repr__(self): 227 | return 'Option(%r, %r, %r, %r)' % (self.short, self.long, 228 | self.argcount, self.value) 229 | 230 | 231 | class Required(BranchPattern): 232 | def match(self, left, collected=None): 233 | collected = [] if collected is None else collected 234 | l = left 235 | c = collected 236 | for pattern in self.children: 237 | matched, l, c = pattern.match(l, c) 238 | if not matched: 239 | return False, left, collected 240 | return True, l, c 241 | 242 | 243 | class Optional(BranchPattern): 244 | def match(self, left, collected=None): 245 | collected = [] if collected is None else collected 246 | for pattern in self.children: 247 | m, left, collected = pattern.match(left, collected) 248 | return True, left, collected 249 | 250 | 251 | class OptionsShortcut(Optional): 252 | """Marker/placeholder for [options] shortcut.""" 253 | 254 | 255 | class OneOrMore(BranchPattern): 256 | def match(self, left, collected=None): 257 | assert len(self.children) == 1 258 | collected = [] if collected is None else collected 259 | l = left 260 | c = collected 261 | l_ = None 262 | matched = True 263 | times = 0 264 | while matched: 265 | # could it be that something didn't match but changed l or c? 266 | matched, l, c = self.children[0].match(l, c) 267 | times += 1 if matched else 0 268 | if l_ == l: 269 | break 270 | l_ = l 271 | if times >= 1: 272 | return True, l, c 273 | return False, left, collected 274 | 275 | 276 | class Either(BranchPattern): 277 | def match(self, left, collected=None): 278 | collected = [] if collected is None else collected 279 | outcomes = [] 280 | for pattern in self.children: 281 | matched, _, _ = outcome = pattern.match(left, collected) 282 | if matched: 283 | outcomes.append(outcome) 284 | if outcomes: 285 | return min(outcomes, key=lambda outcome: len(outcome[1])) 286 | return False, left, collected 287 | 288 | 289 | class Tokens(list): 290 | def __init__(self, source, error=DocoptExit): 291 | self += source.split() if hasattr(source, 'split') else source 292 | self.error = error 293 | 294 | @staticmethod 295 | def from_pattern(source): 296 | source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source) 297 | source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s] 298 | return Tokens(source, error=DocoptLanguageError) 299 | 300 | def move(self): 301 | return self.pop(0) if len(self) else None 302 | 303 | def current(self): 304 | return self[0] if len(self) else None 305 | 306 | 307 | def parse_long(tokens, options): 308 | """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" 309 | long, eq, value = tokens.move().partition('=') 310 | assert long.startswith('--') 311 | value = None if eq == value == '' else value 312 | similar = [o for o in options if o.long == long] 313 | if tokens.error is DocoptExit and similar == []: # if no exact match 314 | similar = [o for o in options if o.long and o.long.startswith(long)] 315 | if len(similar) > 1: # might be simply specified ambiguously 2+ times? 316 | raise tokens.error('%s is not a unique prefix: %s?' % 317 | (long, ', '.join(o.long for o in similar))) 318 | elif len(similar) < 1: 319 | argcount = 1 if eq == '=' else 0 320 | o = Option(None, long, argcount) 321 | options.append(o) 322 | if tokens.error is DocoptExit: 323 | o = Option(None, long, argcount, value if argcount else True) 324 | else: 325 | o = Option(similar[0].short, similar[0].long, 326 | similar[0].argcount, similar[0].value) 327 | if o.argcount == 0: 328 | if value is not None: 329 | raise tokens.error('%s must not have an argument' % o.long) 330 | else: 331 | if value is None: 332 | if tokens.current() in [None, '--']: 333 | raise tokens.error('%s requires argument' % o.long) 334 | value = tokens.move() 335 | if tokens.error is DocoptExit: 336 | o.value = value if value is not None else True 337 | return [o] 338 | 339 | 340 | def parse_shorts(tokens, options): 341 | """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" 342 | token = tokens.move() 343 | assert token.startswith('-') and not token.startswith('--') 344 | left = token.lstrip('-') 345 | parsed = [] 346 | while left != '': 347 | short, left = '-' + left[0], left[1:] 348 | similar = [o for o in options if o.short == short] 349 | if len(similar) > 1: 350 | raise tokens.error('%s is specified ambiguously %d times' % 351 | (short, len(similar))) 352 | elif len(similar) < 1: 353 | o = Option(short, None, 0) 354 | options.append(o) 355 | if tokens.error is DocoptExit: 356 | o = Option(short, None, 0, True) 357 | else: # why copying is necessary here? 358 | o = Option(short, similar[0].long, 359 | similar[0].argcount, similar[0].value) 360 | value = None 361 | if o.argcount != 0: 362 | if left == '': 363 | if tokens.current() in [None, '--']: 364 | raise tokens.error('%s requires argument' % short) 365 | value = tokens.move() 366 | else: 367 | value = left 368 | left = '' 369 | if tokens.error is DocoptExit: 370 | o.value = value if value is not None else True 371 | parsed.append(o) 372 | return parsed 373 | 374 | 375 | def parse_pattern(source, options): 376 | tokens = Tokens.from_pattern(source) 377 | result = parse_expr(tokens, options) 378 | if tokens.current() is not None: 379 | raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) 380 | return Required(*result) 381 | 382 | 383 | def parse_expr(tokens, options): 384 | """expr ::= seq ( '|' seq )* ;""" 385 | seq = parse_seq(tokens, options) 386 | if tokens.current() != '|': 387 | return seq 388 | result = [Required(*seq)] if len(seq) > 1 else seq 389 | while tokens.current() == '|': 390 | tokens.move() 391 | seq = parse_seq(tokens, options) 392 | result += [Required(*seq)] if len(seq) > 1 else seq 393 | return [Either(*result)] if len(result) > 1 else result 394 | 395 | 396 | def parse_seq(tokens, options): 397 | """seq ::= ( atom [ '...' ] )* ;""" 398 | result = [] 399 | while tokens.current() not in [None, ']', ')', '|']: 400 | atom = parse_atom(tokens, options) 401 | if tokens.current() == '...': 402 | atom = [OneOrMore(*atom)] 403 | tokens.move() 404 | result += atom 405 | return result 406 | 407 | 408 | def parse_atom(tokens, options): 409 | """atom ::= '(' expr ')' | '[' expr ']' | 'options' 410 | | long | shorts | argument | command ; 411 | """ 412 | token = tokens.current() 413 | result = [] 414 | if token in '([': 415 | tokens.move() 416 | matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] 417 | result = pattern(*parse_expr(tokens, options)) 418 | if tokens.move() != matching: 419 | raise tokens.error("unmatched '%s'" % token) 420 | return [result] 421 | elif token == 'options': 422 | tokens.move() 423 | return [OptionsShortcut()] 424 | elif token.startswith('--') and token != '--': 425 | return parse_long(tokens, options) 426 | elif token.startswith('-') and token not in ('-', '--'): 427 | return parse_shorts(tokens, options) 428 | elif token.startswith('<') and token.endswith('>') or token.isupper(): 429 | return [Argument(tokens.move())] 430 | else: 431 | return [Command(tokens.move())] 432 | 433 | 434 | def parse_argv(tokens, options, options_first=False): 435 | """Parse command-line argument vector. 436 | If options_first: 437 | argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; 438 | else: 439 | argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; 440 | """ 441 | parsed = [] 442 | while tokens.current() is not None: 443 | if tokens.current() == '--': 444 | return parsed + [Argument(None, v) for v in tokens] 445 | elif tokens.current().startswith('--'): 446 | parsed += parse_long(tokens, options) 447 | elif tokens.current().startswith('-') and tokens.current() != '-': 448 | parsed += parse_shorts(tokens, options) 449 | elif options_first: 450 | return parsed + [Argument(None, v) for v in tokens] 451 | else: 452 | parsed.append(Argument(None, tokens.move())) 453 | return parsed 454 | 455 | 456 | def parse_defaults(doc): 457 | defaults = [] 458 | for s in parse_section('options:', doc): 459 | # FIXME corner case "bla: options: --foo" 460 | _, _, s = s.partition(':') # get rid of "options:" 461 | split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:] 462 | split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])] 463 | options = [Option.parse(s) for s in split if s.startswith('-')] 464 | defaults += options 465 | return defaults 466 | 467 | 468 | def parse_section(name, source): 469 | pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', 470 | re.IGNORECASE | re.MULTILINE) 471 | return [s.strip() for s in pattern.findall(source)] 472 | 473 | 474 | def formal_usage(section): 475 | _, _, section = section.partition(':') # drop "usage:" 476 | pu = section.split() 477 | return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' 478 | 479 | 480 | def extras(help, version, options, doc): 481 | if help and any((o.name in ('-h', '--help')) and o.value for o in options): 482 | print(doc.strip("\n")) 483 | sys.exit() 484 | if version and any(o.name == '--version' and o.value for o in options): 485 | print(version) 486 | sys.exit() 487 | 488 | 489 | class Dict(dict): 490 | def __repr__(self): 491 | return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) 492 | 493 | 494 | def docopt(doc, argv=None, help=True, version=None, options_first=False): 495 | """Parse `argv` based on command-line interface described in `doc`. 496 | `docopt` creates your command-line interface based on its 497 | description that you pass as `doc`. Such description can contain 498 | --options, , commands, which could be 499 | [optional], (required), (mutually | exclusive) or repeated... 500 | Parameters 501 | ---------- 502 | doc : str 503 | Description of your command-line interface. 504 | argv : list of str, optional 505 | Argument vector to be parsed. sys.argv[1:] is used if not 506 | provided. 507 | help : bool (default: True) 508 | Set to False to disable automatic help on -h or --help 509 | options. 510 | version : any object 511 | If passed, the object will be printed if --version is in 512 | `argv`. 513 | options_first : bool (default: False) 514 | Set to True to require options precede positional arguments, 515 | i.e. to forbid options and positional arguments intermix. 516 | Returns 517 | ------- 518 | args : dict 519 | A dictionary, where keys are names of command-line elements 520 | such as e.g. "--verbose" and "", and values are the 521 | parsed values of those elements. 522 | Example 523 | ------- 524 | >>> from docopt import docopt 525 | >>> doc = ''' 526 | ... Usage: 527 | ... my_program tcp [--timeout=] 528 | ... my_program serial [--baud=] [--timeout=] 529 | ... my_program (-h | --help | --version) 530 | ... 531 | ... Options: 532 | ... -h, --help Show this screen and exit. 533 | ... --baud= Baudrate [default: 9600] 534 | ... ''' 535 | >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30'] 536 | >>> docopt(doc, argv) 537 | {'--baud': '9600', 538 | '--help': False, 539 | '--timeout': '30', 540 | '--version': False, 541 | '': '127.0.0.1', 542 | '': '80', 543 | 'serial': False, 544 | 'tcp': True} 545 | See also 546 | -------- 547 | * For video introduction see http://docopt.org 548 | * Full documentation is available in README.rst as well as online 549 | at https://github.com/docopt/docopt#readme 550 | """ 551 | argv = sys.argv[1:] if argv is None else argv 552 | 553 | usage_sections = parse_section('usage:', doc) 554 | if len(usage_sections) == 0: 555 | raise DocoptLanguageError('"usage:" (case-insensitive) not found.') 556 | if len(usage_sections) > 1: 557 | raise DocoptLanguageError('More than one "usage:" (case-insensitive).') 558 | DocoptExit.usage = usage_sections[0] 559 | 560 | options = parse_defaults(doc) 561 | pattern = parse_pattern(formal_usage(DocoptExit.usage), options) 562 | # [default] syntax for argument is disabled 563 | # for a in pattern.flat(Argument): 564 | # same_name = [d for d in arguments if d.name == a.name] 565 | # if same_name: 566 | # a.value = same_name[0].value 567 | argv = parse_argv(Tokens(argv), list(options), options_first) 568 | pattern_options = set(pattern.flat(Option)) 569 | for options_shortcut in pattern.flat(OptionsShortcut): 570 | doc_options = parse_defaults(doc) 571 | options_shortcut.children = list(set(doc_options) - pattern_options) 572 | #if any_options: 573 | # options_shortcut.children += [Option(o.short, o.long, o.argcount) 574 | # for o in argv if type(o) is Option] 575 | extras(help, version, argv, doc) 576 | matched, left, collected = pattern.fix().match(argv) 577 | if matched and left == []: # better error message if left? 578 | return Dict((a.name, a.value) for a in (pattern.flat() + collected)) 579 | raise DocoptExit() -------------------------------------------------------------------------------- /tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NonlinearIdeas/Map-Tiler/a9527ff818615d8dc9b83bc76ced08a465955d82/tileset.png --------------------------------------------------------------------------------