view engine/extensions/serializers/xmlmap.py @ 145:e7a431577c95

Cleaned the basic model up. Code is cleaner now and a bit faster. Some code path were never tested in depth :-( Added a 'time_to_load' attribute to XMLMapLoader, which records the seconds it took to load a map. Down from 6s to 5.6s for reio de hola. Yay!
author phoku@33b003aa-7bff-0310-803a-e67f0ece8222
date Thu, 09 Oct 2008 08:23:13 +0000
parents 5e85737281bc
children 6e7d228def30
line wrap: on
line source

import fife
try:
	import xml.etree.cElementTree as ET
except:
	import xml.etree.ElementTree as ET

import loaders
from serializers import *
import time

FORMAT = '1.0'

class XMLMapLoader(fife.ResourceLoader):
	def __init__(self, engine, callback):
		""" The XMLMapLoader parses the xml map using several section. 
		Each section fires a callback (if given) which can e. g. be
		used to show a progress bar.
		
		The callback sends two values, a string and a float (which shows
		the overall process): callback(string, float)
			
		@param	engine	:	a pointer to fife.engine
		@param	callback:	a callback with two arguments, optional
		"""
		fife.ResourceLoader.__init__(self)
		self.thisown = 0
		
		self.callback = callback

		self.engine = engine
		self.vfs = self.engine.getVFS()
		self.model = self.engine.getModel()
		self.pool = self.engine.getImagePool()
		self.anim_pool = self.engine.getAnimationPool()
		self.map = None
		self.source = None
		self.time_to_load = 0

		self.nspace = None

	def _err(self, msg):
		raise SyntaxError(''.join(['File: ', self.source, ' . ', msg]))

	def loadResource(self, location):
		start_time = time.time()
		self.source = location.getFilename()
		f = self.vfs.open(self.source)
		f.thisown = 1
		tree = ET.parse(f)
		root = tree.getroot()
			
		map = self.parse_map(root)
		self.time_to_load = time.time() - start_time
		return map

	def parse_map(self, mapelt):
		if not mapelt:
			self._err('No <map> element found at top level of map file definition.')
		id,format = mapelt.get('id'),mapelt.get('format')

		if not format == FORMAT: self._err(''.join(['This file has format ', format, ' but this loader has format ', FORMAT]))
		if not id: self._err('Map declared without an identifier.')

		map = None
		try:
			self.map = self.model.createMap(str(id))
			self.map.setResourceFile(self.source)
		except fife.Exception, e: # NameClash appears as general fife.Exception; any ideas?
			print e.getMessage()
			print ''.join(['File: ', self.source, '. The map ', str(id), ' already exists! Ignoring map definition.'])
			return None

		# xml-specific directory imports. This is used by xml savers.
		self.map.importDirs = []

		if self.callback is not None:
			self.callback('created map', float(0.25) )

		self.parse_imports(mapelt, self.map)
		
		self.parse_layers(mapelt, self.map)	
		
		self.parse_cameras(mapelt, self.map)

		return self.map

	def parse_imports(self, mapelt, map):
		if self.callback is not None:		
			tmplist = mapelt.findall('import')
			i = float(0)
		
		for item in mapelt.findall('import'):
			file = item.get('file')
			if file:
				file = reverse_root_subfile(self.source, file)
			dir = item.get('dir')
			if dir:
				dir = reverse_root_subfile(self.source, dir)

			if file and dir:
				loaders.loadImportFile('/'.join(dir, file), self.engine)
			elif file:
				loaders.loadImportFile(file, self.engine)
			elif dir:
				loaders.loadImportDirRec(dir, self.engine)
				map.importDirs.append(dir)
			else:
				print 'Empty import statement?'
				
			if self.callback is not None:
				i += 1				
				self.callback('loaded imports', float( i / float(len(tmplist)) * 0.25 + 0.25 ) )
				
		# cleanup
		if self.callback is not None:
			del tmplist
			del i

	def parse_layers(self, mapelt, map):
		if self.callback is not None:		
			tmplist = mapelt.findall('layer')
			i = float(0)

		for layer in mapelt.findall('layer'):
			id = layer.get('id')
			grid_type = layer.get('grid_type')
			x_scale = layer.get('x_scale')
			y_scale = layer.get('y_scale')
			rotation = layer.get('rotation')
			x_offset = layer.get('x_offset')
			y_offset = layer.get('y_offset')
			pathing = layer.get('pathing')

			if not x_scale: x_scale = 1.0
			if not y_scale: y_scale = 1.0
			if not rotation: rotation = 0.0
			if not x_offset: x_offset = 0.0
			if not y_offset: y_offset = 0.0
			if not pathing: pathing = "cell_edges_only"

			if not id: self._err('<layer> declared with no id attribute.')
			if not grid_type: self._err(''.join(['Layer ', str(id), ' has no grid_type attribute.']))

			allow_diagonals = pathing == "cell_edges_and_diagonals"
			cellgrid = self.model.getCellGrid(grid_type)
			if not cellgrid: self._err('<layer> declared with invalid cellgrid type. (%s)' % grid_type)

			cellgrid.setRotation(float(rotation))
			cellgrid.setXScale(float(x_scale))
			cellgrid.setYScale(float(y_scale))
			cellgrid.setXShift(float(x_offset))
			cellgrid.setYShift(float(y_offset))

			layer_obj = None
			try:
				layer_obj = map.createLayer(str(id), cellgrid)
			except fife.Exception, e:
				print e.getMessage()
				print 'The layer ' + str(id) + ' already exists! Ignoring this layer.'
				continue

			strgy = fife.CELL_EDGES_ONLY
			if pathing == "cell_edges_and_diagonals":
				strgy = fife.CELL_EDGES_AND_DIAGONALS
			if pathing == "freeform":
				strgy = fife.FREEFORM
			layer_obj.setPathingStrategy(strgy)

			self.parse_instances(layer, layer_obj)

			if self.callback is not None:
				i += 1
				self.callback('loaded layer :' + str(id), float( i / float(len(tmplist)) * 0.25 + 0.5 ) )

		# cleanup
		if self.callback is not None:
			del tmplist
			del i

	def parse_instances(self, layerelt, layer):
		instelt = layerelt.find('instances')

		instances = instelt.findall('i')
		instances.extend(instelt.findall('inst'))
		instances.extend(instelt.findall('instance'))
		for instance in instances:

			objectID = instance.get('object')
			if not objectID:
				objectID = instance.get('obj')
			if not objectID:
				objectID = instance.get('o')

			if not objectID: self._err('<instance> does not specify an object attribute.')

			nspace = instance.get('namespace')
			if not nspace:
				nspace = instance.get('ns')
			if not nspace:
				nspace = self.nspace

			if not nspace: self._err('<instance> %s does not specify an object namespace, and no default is available.' % str(objectID))

			self.nspace = nspace

			object = self.model.getObject(str(objectID), str(nspace))
			if not object:
				print ''.join(['Object with id=', str(objectID), ' ns=', str(nspace), ' could not be found. Omitting...'])
				continue

			x = instance.get('x')
			y = instance.get('y')
			z = instance.get('z')
			stackpos = instance.get('stackpos')
			id = instance.get('id')

			if x:
				x = float(x)
				self.x = x
			else:
				self.x = self.x + 1
				x = self.x

			if y:
				y = float(y)
				self.y = y
			else:
				y = self.y

			if z:
				z = float(z)
			else:
				z = 0.0

			if not id:
				id = ''
			else:
				id = str(id)

			inst = layer.createInstance(object, fife.ExactModelCoordinate(x,y,z), str(id))

			rotation = instance.get('r')
			if not rotation:
				rotation = instance.get('rotation')
			if not rotation:
				angles = object.get2dGfxVisual().getStaticImageAngles()
				if angles:
					rotation = angles[0]
				else:
					rotation = 0
			else:
				rotation = int(rotation)
			inst.setRotation(rotation)

			fife.InstanceVisual.create(inst)
			if (stackpos):
				inst.get2dGfxVisual().setStackPosition(int(stackpos))

			if (object.getAction('default')):
				target = fife.Location(layer)
				inst.act('default', target, True)

	def parse_cameras(self, mapelt, map):
		if self.callback is not None:		
			tmplist = mapelt.findall('camera')
			i = float(0)

		for camera in mapelt.findall('camera'):
			id = camera.get('id')
			zoom = camera.get('zoom')
			tilt = camera.get('tilt')
			rotation = camera.get('rotation')
			ref_layer_id = camera.get('ref_layer_id')
			ref_cell_width = camera.get('ref_cell_width')
			ref_cell_height = camera.get('ref_cell_height')
			viewport = camera.get('viewport')

			if not zoom: zoom = 1
			if not tilt: tilt = 0
			if not rotation: rotation = 0

			if not id: self._err('Camera declared without an id.')
			if not ref_layer_id: self._err(''.join(['Camera ', str(id), ' declared with no reference layer.']))
			if not (ref_cell_width and ref_cell_height): self._err(''.join(['Camera ', str(id), ' declared without reference cell dimensions.']))

			try:
				if viewport:
					cam = self.engine.getView().addCamera(str(id), map.getLayer(str(ref_layer_id)),fife.Rect(*[int(c) for c in viewport.split(',')]),fife.ExactModelCoordinate(0,0,0))
				else:
					screen = self.engine.getRenderBackend()
					cam = self.engine.getView().addCamera(str(id), map.getLayer(str(ref_layer_id)),fife.Rect(0,0,screen.getScreenWidth(),screen.getScreenHeight()),fife.ExactModelCoordinate(0,0,0))

				cam.setCellImageDimensions(int(ref_cell_width), int(ref_cell_height))
				cam.setRotation(float(rotation))
				cam.setTilt(float(tilt))
				cam.setZoom(float(zoom))
			except fife.Exception, e:
				print e.getMessage()
				
			if self.callback is not None:
				i += 1
				self.callback('loaded camera: ' +  str(id), float( i / len(tmplist) * 0.25 + 0.75 ) )	
			
		# cleanup
		if self.callback is not None:
			del tmplist
			del i