1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 File input and output for the TileMap class.
24 """
25
26 import os.path
27 import xml.dom.minidom
28 import tilemap
29 import logging
30
31 import preferences
32 import tilemap
33 import shapes
34 import graphics
35 import datafiles
36
37 log = logging.getLogger("mapio")
41 """
42 Brief Description
43 @type fileName: string
44 @param fileName: Placeholder
45 """
46 self.__fileName = fileName
47
49 return self.__fileName
50
51 @staticmethod
53 """
54 Modify this function when adding a new output class
55 @type fileName: string
56 @param fileName: thre name of the file that the map will be saved to
57 """
58 base, ext = os.path.splitext(fileName)
59 ext = str.upper(ext)[1:]
60 if ext == "XML":
61 return XMLMapWriter(fileName)
62 elif ext == "JSON":
63 return JSONMapWriter(fileName)
64 else:
65 raise Exception("No writer class for file type \"" + ext + "\"")
66
68 """
69 Finish writing the map.
70 """
71 raise Exception("MapWriter.finish is abstract")
72
73 - def writeInfo(width, height, tileSize, name, description):
74 """
75 @type width: placeholder
76 @param width: placeholder
77 @type height: placeholder
78 @param height: placeholder
79 @type tileSize: placeholder
80 @param tileSize: placeholder
81 @type name: placeholder
82 @param name: placeholder
83 @type description: placeholder
84 @param description: placeholder
85 """
86 raise Exception("MapWriter.writeInfo is abstract")
87
89 """
90 Brief Description
91 @type layers: Layer[]
92 @param layers: Placeholder
93 """
94 raise Exception("MapWriter.writeLayers is abstract")
95
97 """
98 Brief Description
99 @type shapes: Shape[]
100 @param shapes: Placeholder
101 """
102 raise Exception("MapWriter.writeShapes is abstract")
103
105 """
106 Brief Description
107 @type backgrounds: Parallax[]
108 @param backgrounds: Placeholder
109 @type color: graphics.RGBA
110 @param color: the background fill color
111 """
112 raise Exception("MapWriter.writeBackgrounds is abstract")
113
115 """
116 Writes images to the file
117 @type images: string[]
118 @param images: the images to write
119 """
120 raise Exception("MapWriter.writeImages is abstract")
121
125 MapWriter.__init__(self, fileName)
126 self.__document = xml.dom.minidom.Document()
127 self.__mapElement = self.__document.createElement("tilemap")
128 self.__document.appendChild(self.__mapElement)
129
131 documentString = self.__document.toprettyxml(indent="\t",
132 encoding="UTF-8")
133 outFile = open(self.getFileName(), "w")
134 outFile.write(documentString)
135 outFile.close()
136
137 - def writeInfo(self, width, height, tileSize, name, description):
138 self.__mapElement.setAttribute("width", str(int(width)))
139 self.__mapElement.setAttribute("height", str(int(height)))
140 self.__mapElement.setAttribute("tilesize", str(int(tileSize)))
141 nameElement = self.__document.createElement("name")
142 nameElement.appendChild(self.__document.createTextNode(name))
143 self.__mapElement.appendChild(nameElement)
144 descriptionElement = self.__document.createElement("description")
145 descriptionElement.appendChild(self.__document.createTextNode(
146 description))
147 self.__mapElement.appendChild(descriptionElement)
148
150 lightsElement = self.__document.createElement("lights")
151 for light in lights:
152 self.__writeLight(light, lightsElement)
153 self.__mapElement.appendChild(lightsElement)
154
156 layersElement = self.__document.createElement("layers")
157 for index, layer in enumerate(layers):
158 self.__writeLayer(layer, index, layersElement)
159 self.__mapElement.appendChild(layersElement)
160
162 shapesElement = self.__document.createElement("shapes")
163 for shape in shapeList:
164 if isinstance(shape, shapes.Circle):
165 self.__writeCircle(shape, shapesElement)
166 elif isinstance(shape, shapes.Polygon):
167 self.__writePolygon(shape, shapesElement)
168 else:
169 log.error("Invalid shape type")
170 self.__mapElement.appendChild(shapesElement)
171
173 backgroundsElement = self.__document.createElement("background")
174 backgroundsElement.setAttribute("color", "#%08x" % color.toU32())
175 for background in backgrounds:
176 self.__writeBackground(background, backgroundsElement)
177 self.__mapElement.appendChild(backgroundsElement)
178
180 imagesElement = self.__document.createElement("images")
181 for index, fileName in enumerate(images):
182 self.__writeImage(fileName, index, imagesElement)
183 self.__mapElement.appendChild(imagesElement)
184
186 """
187 Brief Description
188 @type fileName: string
189 @param fileName: Placeholder
190 @type index: int
191 @param index: Placeholder
192 @type e: xml.dom.minidom.Element
193 @param e: Placeholder
194 """
195 imageElement = self.__document.createElement("img")
196 imageElement.setAttribute("index", str(index))
197 if preferences.files["use_prefixes"]:
198 fn = os.path.join(preferences.files["tileset_prefix"],
199 os.path.basename(str(fileName)))
200 else:
201 fn = str(fileName)
202 imageElement.setAttribute("src", fn)
203 e.appendChild(imageElement)
204
206 """
207 Brief Description
208 @type l: Layer
209 @param l: Placeholder
210 @type index: int
211 @param index: Placeholder
212 @type e: xml.dom.minidom.Element
213 @param e: Placeholder
214 """
215 layerElement = self.__document.createElement("layer")
216 layerElement.setAttribute("z", str(int(index)))
217 layerElement.setAttribute("visible", str(bool(l.visible)))
218 layerElement.setAttribute("name", str(l.name))
219 for coords, tile in l.tiles.iteritems():
220 self.__writeTile(tile, coords[0], coords[1], layerElement)
221 e.appendChild(layerElement)
222
224 """
225 Brief Description
226 @type t: Tile
227 @param t: Placeholder
228 @type x: int
229 @param x: Placeholder
230 @type y: int
231 @param y: Placeholder
232 @type e: xml.dom.minidom.Element
233 @param e: Placeholder
234 """
235 tileElement = self.__document.createElement("tile")
236 tileElement.setAttribute("x", str(int(x)))
237 tileElement.setAttribute("y", str(int(y)))
238 ix, iy, index = t.getImageInfo()
239 tileElement.setAttribute("ix", str(int(ix)))
240 tileElement.setAttribute("iy", str(int(iy)))
241 tileElement.setAttribute("img", str(int(index)))
242 e.appendChild(tileElement)
243
245 """
246 Brief Description
247 @type c: Circle
248 @param c: Placeholder
249 @type e: xml.dom.minidom.Element
250 @param e: Placeholder
251 """
252 circleElement = self.__document.createElement("circle")
253 self.__writePoint(c.getCenter(), circleElement)
254 circleElement.setAttribute("radius", str(int(c.getRadius())))
255 circleElement.setAttribute("friction", str(float(c.friction)))
256 circleElement.setAttribute("restitution", str(float(c.restitution)))
257 circleElement.setAttribute("damage", str(float(c.damage)))
258 e.appendChild(circleElement)
259
261 """
262 Brief Description
263 @type p: Polygon
264 @param p: Placeholder
265 @type e: xml.dom.minidom.Element
266 @param e: Placeholder
267 """
268 polygonElement = self.__document.createElement("polygon")
269 polygonElement.setAttribute("friction", str(float(p.friction)))
270 polygonElement.setAttribute("restitution", str(float(p.restitution)))
271 polygonElement.setAttribute("damage", str(float(p.damage)))
272 for point in p.getPoints():
273 self.__writePoint(point, polygonElement)
274 e.appendChild(polygonElement)
275
277 """
278 Brief Description
279 @type p: Point
280 @param p: Placeholder
281 @type e: xml.dom.minidom.Element
282 @param e: Placeholder
283 """
284 pointElement = self.__document.createElement("point")
285 pointElement.setAttribute("x", str(int(p.x)))
286 pointElement.setAttribute("y", str(int(p.y)))
287 e.appendChild(pointElement)
288
290 parallaxElement = self.__document.createElement("parallax")
291 if preferences.file["use_prefixes"]:
292 fn = os.path.join(preferences.files["parallax_prefix"],
293 os.path.basename(str(background.fileName)))
294 else:
295 fn = str(background.fileName)
296 parallaxElement.setAttribute("filename", fn)
297 parallaxElement.setAttribute("vtile", str(bool(background.vTile)))
298 parallaxElement.setAttribute("htile", str(bool(background.hTile)))
299 parallaxElement.setAttribute("vscroll", str(bool(background.vScroll)))
300 parallaxElement.setAttribute("hscroll", str(bool(background.hScroll)))
301 parallaxElement.setAttribute("vscrollspeed", str(float(background.vScrollSpeed)))
302 parallaxElement.setAttribute("hscrollspeed", str(float(background.hScrollSpeed)))
303 parallaxElement.setAttribute("visible", str(bool(background.visible)))
304 e.appendChild(parallaxElement)
305
310
314 MapWriter.__init__(self, fileName)
315 self.__tileData = []
316 self.__width = 0
317 self.__height = 0
318
319 - def writeInfo(self, width, height, tileSize, name, description):
320 self.__width = width
321 self.__height = height
322
323
327
329 st = ""
330 for i in range(self.__width):
331 for j in range(self.__height):
332 if (i, j) in layer.tiles:
333 ix, iy, index = layer.tiles[(i, j)].getImageInfo()
334 st = st + struct.pack('h', (iy * width) + ix)
335
338 """
339 Base class for map readers
340 """
341
343 """
344 @type fileName: string
345 @param fileName: Name of file to open
346 """
347 self.__fileName = fileName
348 self.map = tilemap.TileMap()
349 try:
350 self.file = open(fileName, "r")
351 except IOError, e:
352 print e
353 self.map = None
354
355 @staticmethod
357 """
358 @type fileName: string
359 @param fileName: the name of the file to read
360 @rtype: MapReader
361 @return: a map reader appropriate for the file type
362 """
363 base, ext = os.path.splitext(fileName)
364 ext = str.upper(ext)[1:]
365 if ext == "XML":
366 return XMLMapReader(fileName)
367 elif ext == "JSON":
368 return JSONMapReader(fileName)
369 else:
370 raise Exception("No reader class for file type \"" + ext + "\"")
371
373 """
374 @rtype: TileMap
375 @return: the completed map
376 """
377 return self.map
378
382 MapReader.__init__(self, fileName)
383 if self.map == None:
384 return
385 else:
386 try:
387 self.__document = xml.dom.minidom.parse(self.file)
388 except Exception, ParseException:
389 log.error(parseException)
390 self.map = None
391 return
392 mapElement = self.__document.documentElement
393 self.__processTileMapElement(mapElement)
394 for e in mapElement.getElementsByTagName("name"):
395 self.__processNameElement(e)
396 for e in mapElement.getElementsByTagName("description"):
397 self.__processDescriptionElement(e)
398 for e in mapElement.getElementsByTagName("layers"):
399 for layerElement in e.getElementsByTagName("layer"):
400 self.__processLayer(layerElement)
401 for e in mapElement.getElementsByTagName("background"):
402 self.__processBackground(e)
403 for e in mapElement.getElementsByTagName("shapes"):
404 for p in e.getElementsByTagName("polygon"):
405 self.__processPolygon(p)
406 for c in e.getElementsByTagName("circle"):
407 self.__processCircle(c)
408 for e in mapElement.getElementsByTagName("images"):
409 for i in e.getElementsByTagName("img"):
410 self.__processImages(i)
411 for e in mapElement.getElementsByTagName("lights"):
412 for l in e.getElementsByTagName("light"):
413 self.__processLights(e)
414
416 name = e.childNodes[0]
417 if name.nodeType == name.TEXT_NODE:
418 self.map.name = name.data.strip()
419 else:
420 log.error("<name> contents is not a text node")
421
423 description = e.childNodes[0]
424 if description.nodeType == description.TEXT_NODE:
425 self.map.description = description.data.strip()
426 else:
427 log.error("<description> contents is not a text node")
428
430 if XMLMapReader.__requireAttributes(e, "width", "height", "tilesize") \
431 == False:
432 log.error("Required attribute of tilemap element not found")
433 else:
434 self.map.width = int(e.attributes["width"].value)
435 self.map.height = int(e.attributes["height"].value)
436 self.map.tileSize = int(e.attributes["tilesize"].value)
437
439 """
440 Brief Description
441 @type e: xml.dom.minidom.Element
442 @param e: Placeholder
443 """
444 pass
445
458
460 """
461 Reads through a layer element and adds its information to the maps
462 @type e: xml.dom.minidom.Element
463 @param e: Placeholder
464 """
465 if XMLMapReader.__requireAttributes(e, "z", "visible", "name") == False:
466 logw("Layer has no z value")
467 else:
468 z = int(e.attributes["z"].value)
469
470
471
472 v = bool(e.attributes["visible"].value != "False")
473 n = str(e.attributes["name"].value)
474 self.map.addLayer(n, v, z)
475 for tileElement in e.getElementsByTagName("tile"):
476 t, x, y = self.__processTile(tileElement)
477 self.map.addTile(t, x, y, z)
478
480 """
481 Brief Description
482 @type e: xml.dom.minidom.Element
483 @param e: Placeholder
484 @rtype: (Tile, int, int)
485 @return: Tile tile parsed from e and its x and y coordinates
486 """
487 if XMLMapReader.__requireAttributes(e, "img", "x", "y", "ix", "iy") \
488 == False:
489 log.erorr("Error in __processTile: Required attributes not present")
490 return None
491 else:
492 img = int(e.attributes["img"].value)
493 ix = int(e.attributes["ix"].value)
494 iy = int(e.attributes["iy"].value)
495 x = int(e.attributes["x"].value)
496 y = int(e.attributes["y"].value)
497 t = tilemap.Tile(img, ix, iy)
498 return t, x, y
499
501 """
502 Brief Description
503 @type e: xml.dom.minidom.Element
504 @param e: Placeholder
505 """
506 if XMLMapReader.__requireAttributes(e, "radius", "friction",
507 "restitution", "damage") == False:
508 log.error("Required attributes of circle tag not found")
509 return False
510 else:
511 radius = int(e.attributes["radius"].value)
512 friction = float(e.attributes["friction"].value)
513 restitution = float(e.attributes["restitution"].value)
514 center = None
515 for pointElement in e.getElementsByTagName("point"):
516 center = self.__processPoint(pointElement)
517 c = shapes.Circle(radius)
518 c.setCenter(center)
519 c.friction = float(e.attributes["friction"].value)
520 c.restitution = float(e.attributes["restitution"].value)
521 c.damage = float(e.attributes["damage"].value)
522 self.map.addShape(c)
523
525 """
526 Brief Description
527 @type e: xml.dom.minidom.Element
528 @param e: Placeholder
529 """
530 if XMLMapReader.__requireAttributes(e, "friction",
531 "restitution", "damage") == False:
532 log.error("Required attributes of circle tag not found")
533 return False
534 else:
535 points = []
536 for pointElement in e.getElementsByTagName("point"):
537 points.append(self.__processPoint(pointElement))
538 p = shapes.Polygon(points)
539 p.friction = float(e.attributes["friction"].value)
540 p.restitution = float(e.attributes["restitution"].value)
541 p.damage = float(e.attributes["damage"].value)
542 self.map.addShape(p)
543
545 """
546 Brief Description
547 @type e: xml.dom.minidom.Element
548 @param e: Placeholder
549 """
550 if XMLMapReader.__requireAttributes(e, "x", "y") == False:
551 return None
552 else:
553 x = int(e.attributes["x"].value)
554 y = int(e.attributes["y"].value)
555 return shapes.Point(x, y)
556
558 if XMLMapReader.__requireAttributes(e, "color") == False:
559 log.error("Required attribute of <background> tag not found")
560 return None
561
562 self.map.bgColor = graphics.RGBA()
563 self.map.bgColor.fromU32(int(e.attributes["color"].value[1:], 16))
564
565 for parallaxElement in e.getElementsByTagName("parallax"):
566 self.__processParallax(parallaxElement)
567
569 if XMLMapReader.__requireAttributes(e, "filename", "vtile", "vscroll",
570 "hscroll", "vscrollspeed", "hscrollspeed", "visible") == False:
571 log.error("Required attributes of <parallax> tag not found")
572 return None
573 else:
574 p = tilemap.Parallax()
575 p.fileName = datafiles.getParallaxPath(
576 e.attributes["filename"].value)
577 if os.path.exists(p.fileName) == False:
578 raise datafiles.FileNotFoundError(p.fileName)
579 p.vTile = bool(e.attributes["vtile"].value != "False")
580 p.hTile = bool(e.attributes["htile"].value != "False")
581 p.vScroll = bool(e.attributes["vscroll"].value != "False")
582 p.hScroll = bool(e.attributes["hscroll"].value != "False")
583 p.vScrollSpeed = float(e.attributes["vscrollspeed"].value)
584 p.hScrollSpeed = float(e.attributes["hscrollspeed"].value)
585 p.visible = bool(e.attributes["visible"].value != "False")
586 self.map.backgrounds.append(p)
587
588 @staticmethod
590 """
591 @type element: xml.dom.minidom.Element
592 @param element: the xml element to test
593 @type names: string[]
594 @param names: List of attributes that are required
595 @rtype: bool
596 @return: True if all the required attrubutes were present
597 in the element, False otherwise
598 """
599 for name in names:
600 if name not in element.attributes.keys():
601 log.warn("Attribute " + name + " of " + element.tagName
602 + " not found")
603 return False
604 return True
605
609