source: trunk/game/local_loaders/xmlmap.py @ 264

Revision 264, 12.7 KB checked in by Kaydeth_parpg, 10 years ago (diff)

Ticket #72: Patch by Kaydeth. The separate objects file was merged back into the main map file that FIFE parses to load each Map. The Map, Engine, and World classes were all updated to allow game object creation to happen as the map file is loaded. loaders.py and xmlmap.py were copied from the FIFE code base and made local so that we can now customize how we pass the parsed map and object information to FIFE.

Line 
1#!/usr/bin/python
2
3#   This program is free software: you can redistribute it and/or modify
4#   it under the terms of the GNU General Public License as published by
5#   the Free Software Foundation, either version 3 of the License, or
6#   (at your option) any later version.
7
8#   This program is distributed in the hope that it will be useful,
9#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11#   GNU General Public License for more details.
12
13#   You should have received a copy of the GNU General Public License
14#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16# Most of this code was copied from the FIFE file xmlmap.py
17# It is part of the local code base now so we can customize what happens
18# as we read map files
19
20import fife 
21try:
22    import xml.etree.cElementTree as ET
23except:
24    import xml.etree.ElementTree as ET
25
26import loaders
27from serializers import *
28import time
29
30FORMAT = '1.0'
31
32class XMLMapLoader(fife.ResourceLoader):
33    def __init__(self, engine, data, callback):
34        """ The XMLMapLoader parses the xml map using several section.
35        Each section fires a callback (if given) which can e. g. be
36        used to show a progress bar.
37       
38        The callback sends two values, a string and a float (which shows
39        the overall process): callback(string, float)
40       
41        Inputs:
42            engine = FIFE engine
43            data = Engine object for PARPG data
44            callback = function callback
45        """
46        fife.ResourceLoader.__init__(self)
47        self.thisown = 0
48       
49        self.callback = callback
50
51        self.engine = engine
52        self.data = data
53        self.vfs = self.engine.getVFS()
54        self.model = self.engine.getModel()
55        self.pool = self.engine.getImagePool()
56        self.anim_pool = self.engine.getAnimationPool()
57        self.map = None
58        self.source = None
59        self.time_to_load = 0
60
61        self.nspace = None
62
63    def _err(self, msg):
64        raise SyntaxError(''.join(['File: ', self.source, ' . ', msg]))
65
66    def loadResource(self, location):
67        start_time = time.time()
68        self.source = location.getFilename()
69        f = self.vfs.open(self.source)
70        f.thisown = 1
71        tree = ET.parse(f)
72        root = tree.getroot()
73           
74        map = self.parseMap(root)
75        self.time_to_load = time.time() - start_time
76        return map
77
78    def parseMap(self, map_elt):
79        if not map_elt:
80            self._err('No <map> element found at top level of map file definition.')
81        id,format = map_elt.get('id'),map_elt.get('format')
82
83        if not format == FORMAT: self._err(''.join(['This file has format ', format, ' but this loader has format ', FORMAT]))
84        if not id: self._err('Map declared without an identifier.')
85
86        map = None
87        try:
88            self.map = self.model.createMap(str(id))
89            self.map.setResourceFile(self.source)
90        except fife.Exception, e: # NameClash appears as general fife.Exception; any ideas?
91            print e.getMessage()
92            print ''.join(['File: ', self.source, '. The map ', str(id), ' already exists! Ignoring map definition.'])
93            return None
94
95        # xml-specific directory imports. This is used by xml savers.
96        self.map.importDirs = []
97
98        if self.callback is not None:
99            self.callback('created map', float(0.25) )
100
101        self.parseImports(map_elt, self.map)
102       
103        self.parseLayers(map_elt, self.map)   
104       
105        self.parseCameras(map_elt, self.map)
106
107        return self.map
108
109    def parseImports(self, map_elt, map):
110        parsedImports = {}
111
112        if self.callback:       
113            tmplist = map_elt.findall('import')
114            i = float(0)
115       
116        for item in map_elt.findall('import'):
117            file = item.get('file')
118            if file:
119                file = reverse_root_subfile(self.source, file)
120            dir = item.get('dir')
121            if dir:
122                dir = reverse_root_subfile(self.source, dir)
123
124            # Don't parse duplicate imports
125            if (dir,file) in parsedImports:
126                print "Duplicate import:" ,(dir,file)
127                continue
128            parsedImports[(dir,file)] = 1
129
130            if file and dir:
131                loaders.loadImportFile('/'.join(dir, file), self.engine)
132            elif file:
133                loaders.loadImportFile(file, self.engine)
134            elif dir:
135                loaders.loadImportDirRec(dir, self.engine)
136                map.importDirs.append(dir)
137            else:
138                print 'Empty import statement?'
139               
140            if self.callback:
141                i += 1               
142                self.callback('loaded imports', float( i / float(len(tmplist)) * 0.25 + 0.25 ) )
143
144
145    def parseLayers(self, map_elt, map):
146        if self.callback is not None:       
147            tmplist = map_elt.findall('layer')
148            i = float(0)
149
150        for layer in map_elt.findall('layer'):
151            id = layer.get('id')
152            grid_type = layer.get('grid_type')
153            x_scale = layer.get('x_scale')
154            y_scale = layer.get('y_scale')
155            rotation = layer.get('rotation')
156            x_offset = layer.get('x_offset')
157            y_offset = layer.get('y_offset')
158            pathing = layer.get('pathing')
159
160            if not x_scale: x_scale = 1.0
161            if not y_scale: y_scale = 1.0
162            if not rotation: rotation = 0.0
163            if not x_offset: x_offset = 0.0
164            if not y_offset: y_offset = 0.0
165            if not pathing: pathing = "cell_edges_only"
166
167            if not id: self._err('<layer> declared with no id attribute.')
168            if not grid_type: self._err(''.join(['Layer ', str(id), ' has no grid_type attribute.']))
169
170            allow_diagonals = pathing == "cell_edges_and_diagonals"
171            cellgrid = self.model.getCellGrid(grid_type)
172            if not cellgrid: self._err('<layer> declared with invalid cellgrid type. (%s)' % grid_type)
173
174            cellgrid.setRotation(float(rotation))
175            cellgrid.setXScale(float(x_scale))
176            cellgrid.setYScale(float(y_scale))
177            cellgrid.setXShift(float(x_offset))
178            cellgrid.setYShift(float(y_offset))
179
180            layer_obj = None
181            try:
182                layer_obj = map.createLayer(str(id), cellgrid)
183            except fife.Exception, e:
184                print e.getMessage()
185                print 'The layer ' + str(id) + ' already exists! Ignoring this layer.'
186                continue
187
188            strgy = fife.CELL_EDGES_ONLY
189            if pathing == "cell_edges_and_diagonals":
190                strgy = fife.CELL_EDGES_AND_DIAGONALS
191            if pathing == "freeform":
192                strgy = fife.FREEFORM
193            layer_obj.setPathingStrategy(strgy)
194
195            self.parseInstances(layer, layer_obj)
196
197            if self.callback is not None:
198                i += 1
199                self.callback('loaded layer :' + str(id), float( i / float(len(tmplist)) * 0.25 + 0.5 ) )
200
201        # cleanup
202        if self.callback is not None:
203            del tmplist
204            del i
205
206    def parseInstances(self, layerelt, layer):
207        instelt = layerelt.find('instances')
208
209        instances = instelt.findall('i')
210        instances.extend(instelt.findall('inst'))
211        instances.extend(instelt.findall('instance'))
212        for instance in instances:
213
214            objectID = instance.get('object')
215            if not objectID:
216                objectID = instance.get('obj')
217            if not objectID:
218                objectID = instance.get('o')
219
220            if not objectID: self._err('<instance> does not specify an object attribute.')
221
222            nspace = instance.get('namespace')
223            if not nspace:
224                nspace = instance.get('ns')
225            if not nspace:
226                nspace = self.nspace
227
228            if not nspace: self._err('<instance> %s does not specify an object namespace, and no default is available.' % str(objectID))
229
230            self.nspace = nspace
231
232            object = self.model.getObject(str(objectID), str(nspace))
233            if not object:
234                print ''.join(['Object with id=', str(objectID), ' ns=', str(nspace), ' could not be found. Omitting...'])
235                continue
236
237            x = instance.get('x')
238            y = instance.get('y')
239            z = instance.get('z')
240            stackpos = instance.get('stackpos')
241            id = instance.get('id')
242
243            if x:
244                x = float(x)
245                self.x = x
246            else:
247                self.x = self.x + 1
248                x = self.x
249
250            if y:
251                y = float(y)
252                self.y = y
253            else:
254                y = self.y
255
256            if z:
257                z = float(z)
258            else:
259                z = 0.0
260
261            if not id:
262                id = ''
263            else:
264                id = str(id)
265
266            inst = layer.createInstance(object, fife.ExactModelCoordinate(x,y,z), str(id))
267
268            rotation = instance.get('r')
269            if not rotation:
270                rotation = instance.get('rotation')
271            if not rotation:
272                angles = object.get2dGfxVisual().getStaticImageAngles()
273                if angles:
274                    rotation = angles[0]
275                else:
276                    rotation = 0
277            else:
278                rotation = int(rotation)
279            inst.setRotation(rotation)
280
281            fife.InstanceVisual.create(inst)
282            if (stackpos):
283                inst.get2dGfxVisual().setStackPosition(int(stackpos))
284
285            if (object.getAction('default')):
286                target = fife.Location(layer)
287                inst.act('default', target, True)
288               
289            #Check for PARPG specific object attributes
290            object_type = instance.get('object_type')
291            if ( object_type ):
292                inst_dict = {}
293                inst_dict["type"] = object_type
294                inst_dict["id"] = id
295                inst_dict["xpos"] = x
296                inst_dict["ypos"] = y
297                inst_dict["gfx"] = objectID
298                inst_dict["is_open"] = instance.get('is_open')
299                inst_dict["locked"] = instance.get('locked')
300                inst_dict["text"] = instance.get('text')
301                inst_dict["desc"] = instance.get('desc')
302                self.data.createObject( layer, inst_dict, inst )
303               
304    def parseCameras(self, map_elt, map):
305        if self.callback:       
306            tmplist = map_elt.findall('camera')
307            i = float(0)
308
309        for camera in map_elt.findall('camera'):
310            id = camera.get('id')
311            zoom = camera.get('zoom')
312            tilt = camera.get('tilt')
313            rotation = camera.get('rotation')
314            ref_layer_id = camera.get('ref_layer_id')
315            ref_cell_width = camera.get('ref_cell_width')
316            ref_cell_height = camera.get('ref_cell_height')
317            viewport = camera.get('viewport')
318
319            if not zoom: zoom = 1
320            if not tilt: tilt = 0
321            if not rotation: rotation = 0
322
323            if not id: self._err('Camera declared without an id.')
324            if not ref_layer_id: self._err(''.join(['Camera ', str(id), ' declared with no reference layer.']))
325            if not (ref_cell_width and ref_cell_height): self._err(''.join(['Camera ', str(id), ' declared without reference cell dimensions.']))
326
327            try:
328                if viewport:
329                    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))
330                else:
331                    screen = self.engine.getRenderBackend()
332                    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))
333
334                cam.setCellImageDimensions(int(ref_cell_width), int(ref_cell_height))
335                cam.setRotation(float(rotation))
336                cam.setTilt(float(tilt))
337                cam.setZoom(float(zoom))
338            except fife.Exception, e:
339                print e.getMessage()
340               
341            if self.callback:
342                i += 1
343                self.callback('loaded camera: ' +  str(id), float( i / len(tmplist) * 0.25 + 0.75 ) )   
344           
Note: See TracBrowser for help on using the repository browser.