1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """ main xml parser class for xml map loading """
24
25 import time
26
27 from fife import fife
28
29 from fife.extensions.serializers import ET
30 from fife.extensions.serializers import SerializerError, InvalidFormat
31 from fife.extensions.serializers import NameClash, NotFound, WrongFileType
32
33 from fife.extensions.serializers.xmlobject import XMLObjectLoader
34 from fife.extensions.serializers.xmlanimation import loadXMLAnimation
35 from fife.extensions.serializers.xml_loader_tools import loadImportFile, loadImportDir
36 from fife.extensions.serializers.xml_loader_tools import loadImportDirRec
37 from fife.extensions.serializers.xml_loader_tools import root_subfile, reverse_root_subfile
38
39
40 FORMAT = '1.0'
41
43 """ The B{XMLMapLoader} parses the xml map using several section.
44 Each section fires a callback (if given) which can e. g. be
45 used to show a progress bar.
46
47 The callback sends two values, a string and a float (which shows
48 the overall process): callback(string, float)
49 """
50 - def __init__(self, engine, callback, debug, extensions):
51 """
52 @type engine: object
53 @param engine: a pointer to fife.engine
54 @type callback: function
55 @param callback: a callback with two arguments, optional
56 @type debug: bool
57 @param debug: flag to activate / deactivate print statements
58 @type extensions: dict
59 @param extensions: information package which extension should be activated (lights, sounds)
60 """
61
62
63 self.callback = callback
64 self.debug = debug
65
66 self.engine = engine
67 self.vfs = self.engine.getVFS()
68 self.model = self.engine.getModel()
69 self.image_manager = self.engine.getImageManager()
70 self.anim_pool = None
71
72 self.obj_loader = XMLObjectLoader(engine)
73
74 self.map = None
75 self.source = None
76 self.time_to_load = 0
77
78 self.nspace = None
79
80 self.msg = {}
81 self.msg['map'] = 'created map'
82 self.msg['imports'] = 'loaded imports'
83 self.msg['layer'] = 'loaded layer: %s'
84 self.msg['camera'] = 'loaded camera: %s'
85
86 if 'sound' not in extensions:
87 extensions['sound'] = False
88 if 'lights' not in extensions:
89 extensions['lights'] = False
90
91 self.light_data = {}
92 self.extensions = extensions
93
94 - def _err(self, msg):
95 raise SyntaxError(''.join(['File: ', self.source, ' . ', msg]))
96
98 """ overwrite of B{fife.ResourceLoader}
99
100 @type location: object
101 @param location: path to a map file as a fife.ResourceLocation
102 @type map: object
103 @return map: FIFE map object
104 """
105 start_time = time.time()
106 self.source = location
107 f = self.vfs.open(self.source)
108 f.thisown = 1
109 tree = ET.parse(f)
110 root = tree.getroot()
111
112 map = self.parse_map(root)
113 self.time_to_load = time.time() - start_time
114 return map
115
117 """ start parsing the xml structure and
118 call submethods for turning found tags
119 into FIFE objects and create the map
120
121 @type mapelt: object
122 @param mapelt: ElementTree root
123 @type map: object
124 @return map: FIFE map object
125 """
126 if not mapelt:
127 self._err('No <map> element found at top level of map file definition.')
128 _id, format = mapelt.get('id'), mapelt.get('format')
129
130 if not format == FORMAT: self._err(''.join(['This file has format ', format, ' but this loader has format ', FORMAT]))
131 if not _id: self._err('Map declared without an identifier.')
132
133 map = None
134 try:
135 self.map = self.model.createMap(str(_id))
136 self.map.setFilename(self.source)
137 except fife.Exception, e:
138 print e.getMessage()
139 print ''.join(['File: ', self.source, '. The map ', str(_id), ' already exists! Ignoring map definition.'])
140 return map
141
142
143 self.map.importDirs = []
144
145 if self.callback is not None:
146 self.callback(self.msg['map'], float(0.25) )
147
148 self.parse_imports(mapelt, self.map)
149 self.parse_layers(mapelt, self.map)
150 self.parse_cameras(mapelt, self.map)
151
152
153 if self.light_data:
154 self.create_light_nodes(self.map)
155
156 return self.map
157
159 """ load all objects defined as import into memory
160
161 @type mapelt: object
162 @param mapelt: ElementTree root
163 @type map: object
164 @map map: FIFE map object
165 """
166 parsedImports = {}
167
168 if self.callback:
169 tmplist = mapelt.findall('import')
170 i = float(0)
171
172 for item in mapelt.findall('import'):
173 _file = item.get('file')
174 if _file:
175 _file = reverse_root_subfile(self.source, _file)
176 _dir = item.get('dir')
177 if _dir:
178 _dir = reverse_root_subfile(self.source, _dir)
179
180
181 if (_dir,_file) in parsedImports:
182 if self.debug: print "Duplicate import:" ,(_dir, _file)
183 continue
184 parsedImports[(_dir,_file)] = 1
185
186 if _file and _dir:
187 loadImportFile(self.obj_loader, '/'.join(_dir, _file), self.engine, self.debug)
188 elif _file:
189 loadImportFile(self.obj_loader, _file, self.engine, self.debug)
190 elif _dir:
191 loadImportDirRec(self.obj_loader, _dir, self.engine, self.debug)
192 map.importDirs.append(_dir)
193 else:
194 if self.debug: print 'Empty import statement?'
195
196 if self.callback:
197 i += 1
198 self.callback(self.msg['imports'], float( i / float(len(tmplist)) * 0.25 + 0.25 ) )
199
201 """ create all layers and their instances
202
203 @type mapelt: object
204 @param mapelt: ElementTree root
205 @type map: object
206 @map map: FIFE map object
207 """
208 if self.callback is not None:
209 tmplist = mapelt.findall('layer')
210 i = float(0)
211
212 for layer in mapelt.findall('layer'):
213 _id = layer.get('id')
214 grid_type = layer.get('grid_type')
215
216 if not _id: self._err('<layer> declared with no id attribute.')
217 if not grid_type: self._err(''.join(['Layer ', str(_id), ' has no grid_type attribute.']))
218
219 x_scale = layer.get('x_scale')
220 y_scale = layer.get('y_scale')
221 rotation = layer.get('rotation')
222 x_offset = layer.get('x_offset')
223 y_offset = layer.get('y_offset')
224 z_offset = layer.get('z_offset')
225 pathing = layer.get('pathing')
226 transparency = layer.get('transparency')
227
228 if not x_scale: x_scale = 1.0
229 if not y_scale: y_scale = 1.0
230 if not rotation: rotation = 0.0
231 if not x_offset: x_offset = 0.0
232 if not y_offset: y_offset = 0.0
233 if not z_offset: z_offset = 0.0
234 if not pathing: pathing = "cell_edges_only"
235 if not transparency:
236 transparency = 0
237 else:
238 transparency = int(transparency)
239
240 cellgrid = self.model.getCellGrid(grid_type)
241 if not cellgrid: self._err('<layer> declared with invalid cellgrid type. (%s)' % grid_type)
242
243 cellgrid.setRotation(float(rotation))
244 cellgrid.setXScale(float(x_scale))
245 cellgrid.setYScale(float(y_scale))
246 cellgrid.setXShift(float(x_offset))
247 cellgrid.setYShift(float(y_offset))
248 cellgrid.setZShift(float(z_offset))
249 cellgrid.setAllowDiagonals(pathing != "cell_edges_only");
250
251 layer_obj = None
252 try:
253 layer_obj = map.createLayer(str(_id), cellgrid)
254 except fife.Exception, e:
255 print e.getMessage()
256 print 'The layer ' + str(_id) + ' already exists! Ignoring this layer.'
257
258 continue
259
260 strgy = fife.CELL_EDGES_ONLY
261 if pathing == "cell_edges_and_diagonals":
262 strgy = fife.CELL_EDGES_AND_DIAGONALS
263 if pathing == "freeform":
264 strgy = fife.FREEFORM
265
266 layer_obj.setPathingStrategy(strgy)
267 layer_obj.setLayerTransparency(transparency)
268
269 self.parse_instances(layer, layer_obj)
270
271 if self.extensions['lights']:
272 self.parse_lights(layer, layer_obj)
273 if self.extensions['sound']:
274 self.parse_sounds(layer, layer_obj)
275
276 if self.callback is not None:
277 i += 1
278 self.callback(self.msg['layer'] % str(_id), float( i / float(len(tmplist)) * 0.25 + 0.5 ) )
279
280
281 if self.callback is not None:
282 del tmplist
283 del i
284
286 """ create light nodes
287
288 @type layerelt: object
289 @param layerelt: ElementTree layer branch
290 @type layer: object
291 @map layer: FIFE layer object
292 """
293 _LIGHT_DEFAULT_BLENDING_SRC = -1
294 _LIGHT_DEFAULT_BLENDING_DST = -1
295 _LIGHT_DEFAULT_SUBDIVISIONS = 32
296 _LIGHT_DEFAULT_CAM_ID = 'default'
297 _LIGHT_DEFAULT_INTENSITY = 128
298 _LIGHT_DEFAULT_RADIUS = 10.0
299
300 print "Processing lights ... "
301 lightelt = layerelt.find('lights')
302 if not lightelt:
303 print "\tno lights found on layer %s" % layer.getId()
304 return
305
306 lights = []
307 for attr in ('l', 'light', 'lgt'):
308 lights.extend(lightelt.findall(attr))
309
310 for light in lights:
311 group = light.get('group')
312 if not group:
313 print "Light has no group. Omitting..."
314 continue
315
316 blending_src = light.get('src')
317 if not blending_src:
318 blending_src = _LIGHT_DEFAULT_BLENDING_SRC
319 blending_dst = light.get('dst')
320 if not blending_dst:
321 blending_dst = _LIGHT_DEFAULT_BLENDING_DST
322
323 _x = light.get('x')
324 if not _x: _x = 0
325 _y = light.get('y')
326 if not _y: _y = 0
327 _z = light.get('y')
328 if not _z: _z = 0
329
330 node = {}
331 node['blending_src'] = int(blending_src)
332 node['blending_dst'] = int(blending_dst)
333 node['layer'] = layer.getId()
334 node['position'] = int(_x), int(_y), int(_z)
335
336
337 instance_id = light.get('instance')
338 node['instance'] = None
339 if instance_id and layer.getInstance(instance_id):
340 node['instance'] = instance_id
341
342 type = light.get('type')
343 if type:
344 s_ref = light.get('s_ref')
345 if not s_ref: s_ref = -1
346 node['s_ref'] = int(s_ref)
347 a_ref = light.get('a_ref')
348 if not a_ref: a_ref = 0.0
349 node['a_ref'] = float(a_ref)
350
351 if type == 'image':
352 image = light.get('image')
353 if not image:
354 print "Light has no image. Omitting..."
355 continue
356 node['type'] = 'image'
357 image = reverse_root_subfile(self.source, image)
358 img = self.image_manager.create(image)
359 node['image'] = img
360 elif type == 'animation':
361 animation = light.get('animation')
362 if not animation:
363 print "Light has no animation. Omitting..."
364 continue
365 node['type'] = 'animation'
366 animation = reverse_root_subfile(self.source, animation)
367 anim = loadXMLAnimation(self.engine, animation)
368 node['animation'] = anim
369 elif type == 'simple':
370 node['type'] = type
371 radius = light.get('radius')
372 if not radius: radius = _LIGHT_DEFAULT_RADIUS
373 node['radius'] = float(radius)
374
375 subdivisions = light.get('subdivisions')
376 if not subdivisions:
377 subdivisions = _LIGHT_DEFAULT_SUBDIVISIONS
378 node['subdivisions'] = int(subdivisions)
379
380 intensity = light.get('intensity')
381 if not intensity:
382 intensity = _LIGHT_DEFAULT_INTENSITY
383 node['intensity'] = int(intensity)
384
385 xstretch = light.get('xstretch')
386 if not xstretch: xstretch = 1.0
387 ystretch = light.get('ystretch')
388 if not ystretch: ystretch = 1.0
389 node['stretch'] = float(xstretch), float(ystretch)
390
391 color = light.get('color')
392 if not color: color = '%d,%d,%d' % (255, 255, 255)
393 node['color'] = ([int(c) for c in color.split(',')])
394
395 else:
396 continue
397
398 cam_id = light.get('camera_id')
399 if not cam_id: cam_id = _LIGHT_DEFAULT_CAM_ID
400
401 if not cam_id in self.light_data:
402 self.light_data[cam_id] = {}
403 if group not in self.light_data[cam_id]:
404 self.light_data[cam_id][group] = []
405
406 self.light_data[cam_id][group].append(node)
407
408 for camera, groups in self.light_data.iteritems():
409 print "Lights for camera %s" % camera
410 for group, lights in groups.iteritems():
411 print group, lights
412
414 """ create sound emitter
415
416 FIXME:
417 - FIFE has a hard limit of sound emitters
418 how should we load emitters here?
419 - my first thought: collect a list of sound
420 files & data for emitter creation,
421 then let the client decide what to do with it
422
423 @type layerelt: object
424 @param layerelt: ElementTree layer branch
425 @type layer: object
426 @map layer: FIFE layer object
427 """
428
429 pass
430
432 """ create all layers and their instances
433
434 @type layerelt: object
435 @param layerelt: ElementTree layer branch
436 @type layer: object
437 @map layer: FIFE layer object
438 """
439 instelt = layerelt.find('instances')
440
441 instances = []
442 for attr in ('i', 'inst', 'instance'):
443 instances.extend(instelt.findall(attr))
444
445 for instance in instances:
446 _id = instance.get('id')
447 if not _id:
448 _id = ''
449
450 objectID = ''
451 for attr in ('o', 'object', 'obj'):
452 objectID = instance.get(attr)
453 if objectID: break
454 if not objectID: self._err('<instance> %s does not specify an object attribute.' % str(objectID))
455 objectID = str(objectID)
456
457 nspace = ''
458 for attr in ('namespace', 'ns'):
459 nspace = instance.get(attr)
460 if nspace: break
461
462 if not nspace and self.nspace:
463 nspace = self.nspace
464 if not nspace and not self.nspace: self._err('<instance> %s does not specify an object namespace, and no default is available.' % str(objectID))
465 nspace = str(nspace)
466 self.nspace = nspace
467
468
469 object = self.model.getObject(objectID, nspace)
470 if not object:
471 print "Object with id=%s, ns=%s could not be found. Omitting..." % (objectID, nspace)
472 continue
473
474 x = instance.get('x')
475 if x: self.x = x = float(x)
476 else: x = self.x
477
478 y = instance.get('y')
479 if y: self.y = y = float(y)
480 else: y = self.y
481
482 z = instance.get('z')
483 if z: z = float(z)
484 else: z = 0.0
485
486 inst = layer.createInstance(object, fife.ExactModelCoordinate(x,y,z), _id)
487
488 rotation = 0
489 for attr in ('r', 'rotation'):
490 rotation = instance.get(attr)
491 if rotation: break
492 if not rotation:
493 angles = object.get2dGfxVisual().getStaticImageAngles()
494 if angles:
495 rotation = angles[0]
496 else:
497 rotation = 0
498 else:
499 rotation = int(rotation)
500 inst.setRotation(rotation)
501
502 over_block = instance.get('override_blocking')
503 if over_block is not None:
504 inst.setOverrideBlocking(bool(over_block))
505 blocking = instance.get('blocking')
506 if blocking is not None:
507 inst.setBlocking(bool(int(blocking)))
508
509 fife.InstanceVisual.create(inst)
510
511 stackpos = instance.get('stackpos')
512 if stackpos:
513 inst.get2dGfxVisual().setStackPosition(int(stackpos))
514
515 if (object.getAction('default')):
516 target = fife.Location(layer)
517 inst.act('default', target, True)
518
520 """ create all cameras and activate them
521
522 FIXME:
523 - should the cameras really be enabled here?
524 IMO that's part of the setup within a client
525 (we just _load_ things here)
526
527 @type mapelt: object
528 @param mapelt: ElementTree root
529 @type map: object
530 @map map: FIFE map object
531 """
532 if self.callback:
533 tmplist = mapelt.findall('camera')
534 i = float(0)
535
536 for camera in mapelt.findall('camera'):
537 _id = camera.get('id')
538 zoom = camera.get('zoom')
539 tilt = camera.get('tilt')
540 rotation = camera.get('rotation')
541 ref_layer_id = camera.get('ref_layer_id')
542 ref_cell_width = camera.get('ref_cell_width')
543 ref_cell_height = camera.get('ref_cell_height')
544 viewport = camera.get('viewport')
545 light_color = camera.get('light_color')
546
547 if not zoom: zoom = 1
548 if not tilt: tilt = 0
549 if not rotation: rotation = 0
550
551 if not _id: self._err('Camera declared without an id.')
552 if not ref_layer_id: self._err(''.join(['Camera ', str(_id), ' declared with no reference layer.']))
553 if not (ref_cell_width and ref_cell_height): self._err(''.join(['Camera ', str(_id), ' declared without reference cell dimensions.']))
554
555 try:
556 if viewport:
557 cam = map.addCamera(str(_id), map.getLayer(str(ref_layer_id)),fife.Rect(*[int(c) for c in viewport.split(',')]))
558
559 else:
560 screen = self.engine.getRenderBackend()
561 cam = map.addCamera(str(_id), map.getLayer(str(ref_layer_id)),fife.Rect(0,0,screen.getScreenWidth(),screen.getScreenHeight()))
562
563 renderer = fife.InstanceRenderer.getInstance(cam)
564 renderer.activateAllLayers(map)
565
566 except fife.Exception, e:
567 print e.getMessage()
568
569 if light_color: cam.setLightingColor(*[float(c) for c in light_color.split(',')])
570 cam.setCellImageDimensions(int(ref_cell_width), int(ref_cell_height))
571 cam.setRotation(float(rotation))
572 cam.setTilt(float(tilt))
573 cam.setZoom(float(zoom))
574
575 renderer = fife.InstanceRenderer.getInstance(cam)
576 renderer.activateAllLayers(map)
577
578 if self.callback:
579 i += 1
580 self.callback(self.msg['camera'] % str(_id), float( i / len(tmplist) * 0.25 + 0.75 ) )
581
583 """ loop through all preloaded lights and create them
584 according to their data
585
586 @type map: object
587 @param map: FIFE map object
588 """
589 cameras = [i.getId() for i in map.getCameras()]
590 renderers = {}
591 default_cam = map.getCameras()[0].getId()
592
593 def add_simple_light(group, renderer, node, data):
594 """ add a node as simple light to the renderer
595
596 @type group: string
597 @param group: name of the light group
598 @type renderer: object
599 @param renderer: fife.LightRenderer instance
600 @type node: object
601 @param node: fife.RendererNode instance
602 @type data: dict
603 @param data: all data for the light type creation
604 """
605 if not node: return
606 if not group: return
607 renderer.addSimpleLight(
608 group,
609 node,
610 data['intensity'],
611 data['radius'],
612 data['subdivisions'],
613 data['stretch'][0],
614 data['stretch'][1],
615 data['color'][0],
616 data['color'][1],
617 data['color'][2],
618 data['blending_src'],
619 data['blending_dst'],
620 )
621 if data['s_ref'] is not -1:
622 add_stencil_test(group, renderer, data)
623 def add_animated_lightmap(group, renderer, node, data):
624 """ add a node as animated lightmap to the renderer
625
626 @type group: string
627 @param group: name of the light group
628 @type renderer: object
629 @param renderer: fife.LightRenderer instance
630 @type node: object
631 @param node: fife.RendererNode instance
632 @type data: dict
633 @param data: all data for the light type creation
634 """
635 if not node: return
636 if not group: return
637 renderer.addAnimation(
638 group,
639 node,
640 data['animation'],
641 data['blending_src'],
642 data['blending_dst'],
643 )
644 if data['s_ref'] is not -1:
645 add_stencil_test(group, renderer, data)
646 def add_lightmap(group, renderer, node, data):
647 """ add a node as lightmap to the renderer
648
649 @type group: string
650 @param group: name of the light group
651 @type renderer: object
652 @param renderer: fife.LightRenderer instance
653 @type node: object
654 @param node: fife.RendererNode instance
655 @type data: dict
656 @param data: all data for the light type creation
657 """
658 if not node: return
659 if not group: return
660 renderer.addImage(
661 group,
662 node,
663 data['image'],
664 data['blending_src'],
665 data['blending_dst'],
666 )
667 if data['s_ref'] is not -1:
668 add_stencil_test(group, renderer, data)
669 def add_stencil_test(group, renderer, data):
670 """ add a stencil test to a group
671
672 @type group: string
673 @param group: name of the light group
674 @type renderer: object
675 @param renderer: fife.LightRenderer instance
676 @type data: dict
677 @param data: all data for the light type creation
678 """
679 if not group: return
680 renderer.addStencilTest(
681 group,
682 data['s_ref'],
683 data['a_ref'],
684 )
685
686 def create_node(instance=None, point=None, layer=None):
687 """ creates a node of one of these types:
688
689 - attached to an instance
690 - attached to an instance with offset
691 - attached to a point
692
693 FIXME:
694 - add location node
695
696 @type: instance: object
697 @param instance: fife instance object
698 @type point: tuple
699 @param point: x,y,z tuple
700 @type layer: object
701 @param layer: fife layer object
702 """
703 node = None
704 if not layer: return node
705
706
707 if point and not instance:
708 point = fife.Point(point[0], point[1])
709 node = fife.RendererNode(point);
710
711 if instance and point:
712 node = fife.RendererNode(instance, layer, fife.Point(point[0], point[1]))
713
714 if instance and not point:
715 node = fife.RendererNode(instance, layer)
716
717 if node:
718 node.thisown = 0
719
720 return node
721
722 def dump_data():
723 """ dump all loaded data """
724 for camera, groups in self.light_data.iteritems():
725 print "Lights for camera %s" % camera
726 for group, lights in groups.iteritems():
727 print group, lights
728
729
730 for _id in cameras:
731 camera = map.getCamera(_id)
732 renderers[_id] = fife.LightRenderer.getInstance(camera)
733
734
735 for camera, groups in self.light_data.iteritems():
736 for group, lights in groups.iteritems():
737 for light in lights:
738 instance = None
739 layer = map.getLayer(light['layer'])
740 if light['instance']:
741 instance = layer.getInstance(light['instance'])
742 position = light['position']
743 node = create_node(instance, position, layer)
744
745
746 if not node: continue
747
748 if camera == 'default':
749 renderer = renderers[default_cam]
750 else:
751 renderer = renderers[camera]
752
753 if light['type'] == 'simple':
754 add_simple_light(group, renderer, node, light)
755 elif light['type'] == 'image':
756 add_lightmap(group, renderer, node, light)
757 elif light['type'] == 'animation':
758 add_animated_lightmap(group, renderer, node, light)
759
760
761