source: trunk/game/scripts/gamemodel.py @ 595

Revision 595, 27.5 KB checked in by beliar, 9 years ago (diff)

Patch by Beliar.

  • Items lying on the map can now be picked up. (Items can't be dropped on the map yet though)
  • Every Agent is now given a unique ID if the original is already used. This is needed for the items to be correctly removed from the map in savegames. Note: Savegames have NOT become incompatible in this revision.
  • Property svn:eol-style set to native
Line 
1#!/usr/bin/env python
2
3#   This file is part of PARPG.
4
5#   PARPG is free software: you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation, either version 3 of the License, or
8#   (at your option) any later version.
9
10#   PARPG is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14
15#   You should have received a copy of the GNU General Public License
16#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
17
18# there should be NO references to FIFE here!
19import sys
20import os.path
21from gamestate import GameState
22from objects import createObject
23from objects.composed import CarryableItem
24from gamemap import GameMap
25from fife import fife
26from common.utils import locateFiles
27from common.utils import parseBool
28from inventory import Inventory
29from copy import deepcopy
30
31try:
32    import xml.etree.cElementTree as ElementTree
33except ImportError:
34    import xml.etree.ElementTree as ElementTree
35
36import yaml
37
38class GameModel(object):
39    """GameModel holds the logic for the game.
40       Since some data (object position and so forth) is held in the
41       fife, and would be pointless to replicate, we hold a instance of
42       the fife view here. This also prevents us from just having a
43       function heavy controller."""
44    ALL_AGENTS_KEY = "All"
45    MAX_ID_NUMBER = 1000
46   
47    def __init__(self, engine, settings):
48        """Initialize the instance.
49        @param engine: A fife.Engine object
50        @type emgome: fife.Engine
51        @param setting: The applications settigns
52        @type setting: fife_settings.Setting
53        @return: None"""
54        self.map_change = False
55        self.load_saver = False
56        self.savegame = None
57        self.game_state = GameState(quests_dir = settings.get("PARPG",
58                                                             "QuestsDirectory"))
59        #self.game_state.quest_engine =
60        #self.game_state.quest_engine.readQuests()
61        self.pc_run = 1
62        self.target_position = None
63        self.target_map_name = None
64        self.object_db = {}
65        self.active_map = None
66        self.map_files = {}
67        self.agents = {}
68        self.agents[self.ALL_AGENTS_KEY] = {}
69        self.engine = engine
70        self.fife_model = engine.getModel()
71        self.game_state.maps_file = "maps/maps.yaml"
72        self.all_agents_file = "maps/all_agents.yaml"
73        self.object_db_file = "objects/object_database.yaml"
74        self.agents_directory = "objects/"
75        self.dialogues_directory = "dialogue"
76        self.dialogues = {}
77        self.agent_import_files = {}
78        self.settings = settings
79
80    def checkAttributes(self, attributes):
81        """Checks for attributes that where not given in the map file
82        and fills them with values from the object database
83        @param attributes: attributes to check
84        @type attributes: Dictionary
85        @return: The modified attributes""" 
86        from copy import deepcopy       
87        if attributes.has_key("object_type"):
88            class_name = attributes.pop("object_type")
89        else:
90            class_name = attributes["type"]
91        if self.object_db.has_key(class_name):
92            db_attributes = deepcopy(self.object_db[class_name])
93            for key in db_attributes.keys():
94                if attributes.has_key(key):
95                    attributes[key] = attributes[key] or db_attributes[key]
96                else:
97                    attributes[key] = db_attributes[key]
98        return attributes
99   
100    def isIDUsed(self, ID):
101        if self.game_state.hasObject(ID):
102            return True
103        for namespace in self.agents:
104            if ID in self.agents[namespace]:
105                return True
106        return False
107   
108    def createUniqueID(self, ID):
109        if self.isIDUsed(ID):
110            id_number = 1
111            while self.isIDUsed(ID + "_" + str(id_number)):
112                id_number += 1
113                if id_number > self.MAX_ID_NUMBER:
114                    raise ValueError(
115                        "Number exceeds MAX_ID_NUMBER:" + str(self.MAX_ID_NUMBER))
116           
117            ID = ID + "_" + str(id_number)
118        return ID
119
120    def createContainerObject(self, attributes):
121        """Create an object that can be stored in
122        an container and return it
123        @param attributes: Dictionary of all object attributes
124        @type attributes: Dictionary
125        @return: The created object """
126        # create the extra data
127        extra = {}
128        extra['controller'] = self
129        attributes = self.checkAttributes(attributes)
130       
131        info = {}
132        info.update(attributes)
133        info.update(extra)
134        ID = info.pop("id") if info.has_key("id") else info.pop("ID")
135        if not info.has_key("item_id"):
136            info['item_id'] = ID
137        ID = self.createUniqueID(ID)
138        new_item = CarryableItem(ID = ID, **info) 
139        self.game_state.addObject(None, new_item)
140        return new_item
141     
142    def createInventoryObject(self, container, attributes):
143        """Create an inventory object and place it into a container
144           @type container: base.Container
145           @param container: Container where the item is on
146           @type attributes: Dictionary
147           @param attributes: Dictionary of all object attributes
148           @return: None"""
149        index = attributes.pop("index") if attributes.has_key("index") else None
150        slot = attributes.pop("slot") if attributes.has_key("slot") else None
151        obj = self.createContainerObject(attributes)       
152        #obj = createObject(attributes, extra)
153        if slot:
154            container.moveItemToSlot(obj, slot)
155        else:
156            container.placeItem(obj, index)
157   
158    def deleteObject(self, object_id):
159        """Removes an object from the game
160        @param object_id: ID of the object
161        @type object_id: str """
162        del self.agents["All"][object_id]
163        self.game_state.deleteObject(object_id)
164       
165    def save(self, path, filename):
166        """Writes the saver to a file.
167           @type filename: string
168           @param filename: the name of the file to write to
169           @return: None"""
170        fname = '/'.join([path, filename])
171        try:
172            save_file = open(fname, 'w')
173        except(IOError):
174            sys.stderr.write("Error: Can't create save game: " + fname + "\n")
175            return
176        save_state = {}
177        save_state["Agents"] = {}
178        for map_name in self.agents:
179            if map_name == self.ALL_AGENTS_KEY:
180                continue
181            agents_dict = {}
182            for agent in self.agents[map_name]:
183                agent_obj = self.game_state.getObjectById(agent, map_name)
184                agent_inst = self.game_state.maps[map_name].\
185                                    agent_layer.getInstance(agent)
186                agent_dict = self.agents[map_name][agent]
187                agent_dict.update(agent_obj.getStateForSaving())
188                agent_dict["Rotation"] = agent_inst.getRotation()
189                agents_dict[agent] = agent_dict
190            save_state["Agents"][map_name] = agents_dict
191        agents_dict = {}
192        for agent in self.agents["All"]:
193            map_name = self.agents["All"][agent]["Map"]
194            agent_dict = self.agents["All"][agent]
195            agent_obj = None
196            if agent == "PlayerCharacter":
197                agent_obj = self.game_state.player_character
198            else:
199                agent_obj = self.game_state.getObjectById(agent, map_name)
200            if agent_obj:
201                agent_inst = self.game_state.maps[map_name].\
202                                    agent_layer.getInstance(agent)
203                agent_dict.update(agent_obj.getStateForSaving())
204                agent_dict["Rotation"] = agent_inst.getRotation()
205                agent_dict["MapName"] = map_name
206            agents_dict[agent] = agent_dict
207        save_state["Agents"]["All"] = agents_dict
208        save_state["GameState"] = self.game_state.getStateForSaving()
209        yaml.dump(save_state, save_file)
210       
211        save_file.close()       
212
213    def load(self, path, filename):
214        """Loads a saver from a file.
215           @type filename: string
216           @param filename: the name of the file (including path) to load from
217           @return: None"""
218        fname = '/'.join([path, filename])
219
220        try:
221            load_file = open(fname, 'r')
222        except(IOError):
223            sys.stderr.write("Error: Can't find save game file\n")
224            return       
225        self.deleteMaps()
226        self.clearAgents()
227       
228        save_state = yaml.load(load_file)
229        self.game_state.restoreFromState(save_state["GameState"])
230        maps = save_state["Agents"]
231        for map_name in maps:
232            for agent_name in maps[map_name]:
233                agent = {agent_name:maps[map_name][agent_name]}
234                self.addAgent(map_name, agent)
235               
236        # Load the current map
237        if self.game_state.current_map_name:
238            self.loadMap(self.game_state.current_map_name)         
239        load_file.close()
240       
241
242        # Recreate all the behaviours. These can't be saved because FIFE
243        # objects cannot be pickled
244       
245        self.placeAgents()
246        self.placePC()
247     
248        # In most maps we'll create the PlayerCharacter Instance internally.
249        # In these cases we need a target position
250         
251    def teleport(self, agent, position):
252        """Called when a an agent is moved instantly to a new position.
253        The setting of position may wan to be created as its own method down the road.
254        @type position: String Tuple
255        @param position: X,Y coordinates passed from engine.changeMap
256        @return: fife.Location"""
257        print position
258        coord = fife.DoublePoint3D(float(position[0]), float(position[1]), 0)
259        location = fife.Location(self.active_map.agent_layer)
260        location.setMapCoordinates(coord)
261        agent.teleport(location)         
262               
263    def getObjectAtCoords(self, coords):
264        """Get the object which is at the given coords
265        @type coords: fife.Screenpoint
266        @param coords: Coordinates where to check for an object
267        @rtype: fife.Object
268        @return: An object or None"""
269        instances = self.active_map.cameras[
270                                            self.active_map.my_cam_id].\
271            getMatchingInstances(coords, self.active_map.agent_layer)
272        # no object returns an empty tuple
273        if(instances != ()):
274            front_y = 0
275           
276
277            for obj in instances:
278                # check to see if this in our list at all
279                if(self.objectActive(obj.getId())):
280                    # check if the object is on the foreground
281                    obj_map_coords = \
282                                      obj.getLocation().getMapCoordinates()
283                    obj_screen_coords = self.active_map.\
284                        cameras[self.active_map.my_cam_id]\
285                        .toScreenCoordinates(obj_map_coords)
286
287                    if obj_screen_coords.y > front_y:
288                        #Object on the foreground
289                        front_y = obj_screen_coords.y
290                        return obj
291                    else:
292                        return None
293        else:
294            return None
295
296    def getCoords(self, click):
297        """Get the map location x, y coordinates from the screen coordinates
298           @type click: fife.ScreenPoint
299           @param click: Screen coordinates
300           @rtype: fife.Location
301           @return: The map coordinates"""
302        coord = self.active_map.cameras[self.active_map.my_cam_id].\
303                    toMapCoordinates(click, False)
304        coord.z = 0
305        location = fife.Location(self.active_map.agent_layer)
306        location.setMapCoordinates(coord)
307        return location
308
309    def togglePause(self):
310        """ Pause/Unpause the game.
311            @return: nothing"""
312        self.active_map.togglePause()
313   
314    def readMapFiles(self):
315        """Read all a available map-files and store them"""
316        maps_data = file(self.game_state.maps_file)
317        self.map_files = yaml.load(maps_data)["Maps"]
318   
319    def addAgent(self, namespace, agent):
320        """Adds an agent to the agents dictionary
321        @param namespace: the namespace where the agent is to be added to
322        @type namespace: str
323        @param agent: The agent to be added
324        @type agent: dict """
325        from local_loaders.loaders import loadImportFile
326        if not self.agents.has_key(namespace):
327            self.agents[namespace] = {}
328           
329        agent_values = agent.values()[0]
330        unique_agent_id = self.createUniqueID(agent.keys()[0])
331        del agent[agent.keys()[0]]
332        agent[unique_agent_id] = agent_values
333        self.agents[namespace].update(agent)
334        object_model = ""
335        if agent_values.has_key("ObjectModel"): 
336            object_model =  agent_values["ObjectModel"]
337        elif agent_values["ObjectType"] == "MapItem":
338            object_model = self.object_db[agent_values["ItemType"]]["gfx"]
339        else:
340            object_model = self.object_db[agent_values["ObjectType"]]["gfx"]
341        import_file = self.agent_import_files[object_model]
342        loadImportFile(import_file, self.engine)
343       
344    def readAgentsOfMap(self, map_name):
345        """Read the agents of the map
346        @param map_name: Name of the map
347        @type map_name: str """
348        #Get the agents of the map       
349        map_agents_file = self.map_files[map_name].\
350                            replace(".xml", "_agents.yaml")   
351        agents_data = file(map_agents_file)
352        agents = yaml.load_all(agents_data)
353        for agent in agents:
354            if not agent == None:
355                self.addAgent(map_name, agent) 
356   
357    def readAllAgents(self):
358        """Read the agents of the all_agents_file and store them"""
359        agents_data = file(self.all_agents_file)
360        agents = yaml.load_all(agents_data)
361        for agent in agents:
362            if not agent == None:
363                self.addAgent(self.ALL_AGENTS_KEY, agent) 
364               
365    def getAgentsOfMap(self, map_name):
366        """Returns the agents that are on the given map
367        @param map_name: Name of the map
368        @type map_name: str
369        @return: A dictionary with the agents of the map"""
370        if not self.agents.has_key(map_name):
371            return {}
372        ret_dict = self.agents[map_name].copy()
373        for agent_name, agent_value in self.agents[self.ALL_AGENTS_KEY]\
374                                                .iteritems():
375            if agent_value["Map"] == map_name:
376                ret_dict[agent_name] = agent_value
377        return ret_dict
378               
379    def getAgentsOfActiveMap(self):
380        """Returns the agents that are on active map
381        @return: A dictionary with the agents of the map """
382        return self.getAgentsOfMap(self.active_map.map.getId())
383
384    def clearAgents(self):
385        """Resets the agents dictionary"""
386        self.agents = {}
387        self.agents[self.ALL_AGENTS_KEY] = {}
388   
389    def loadMap(self, map_name):
390        """Load a new map.
391           @type map_name: string
392           @param map_name: Name of the map to load
393           @return: None"""
394        if not map_name in self.game_state.maps: 
395            map_file = self.map_files[map_name]
396            new_map = GameMap(self.engine, self)
397            self.game_state.maps[map_name] = new_map
398            new_map.load(map_file)
399   
400    def createAgent(self, agent, inst_id):
401        object_type = agent["ObjectType"]
402        object_id = agent["ObjectModel"] \
403                                if agent.has_key("ObjectModel") \
404                                else None
405        if object_id == None:
406            if object_type == "MapItem":
407                object_id = self.object_db[agent["ItemType"]]["gfx"]
408            else:
409                object_id = self.object_db[object_type]["gfx"]
410        map_obj = self.fife_model.getObject(str(object_id), "PARPG")
411        if not map_obj:
412            print ''.join(['Object with inst_id=', str(object_id), 
413                           ' ns=PARPG', \
414                           ' could not be found. Omitting...'])
415
416        x_pos = agent["Position"][0]
417        y_pos = agent["Position"][1]
418        z_pos = agent["Position"][2] if len(agent["Position"]) == 3 \
419                                        else -0.1 if object_type == "MapItem" \
420                                        else 0.0 
421        stack_pos = agent["Stackposition"] if \
422                        agent.has_key("StackPosition") \
423                        else None
424        inst = self.active_map.agent_layer.\
425                        createInstance(map_obj,
426                                       fife.ExactModelCoordinate(x_pos, 
427                                                                 y_pos, 
428                                                                 z_pos),
429                                       inst_id)
430        inst.setId(inst_id)
431
432        rotation = agent["Rotation"]
433        inst.setRotation(rotation)
434
435        fife.InstanceVisual.create(inst)
436        if (stack_pos):
437            inst.get2dGfxVisual().setStackPosition(int(stack_pos))
438
439        if (map_obj.getAction('default')):
440            target = fife.Location(self.active_map.agent_layer)
441            inst.act('default', target, True)
442           
443        inst_dict = {}
444        inst_dict["id"] = inst_id
445        inst_dict["type"] = object_type
446        inst_dict["xpos"] = x_pos
447        inst_dict["ypos"] = y_pos
448        inst_dict["gfx"] = object_id
449        inst_dict["is_open"] = parseBool(agent["Open"]) \
450                                if agent.has_key("Open") \
451                                else False
452        inst_dict["locked"] = parseBool(agent["Locked"]) \
453                                if agent.has_key("Locked") \
454                                else False
455        inst_dict["name"] = agent["ViewName"]
456        inst_dict["real_name"] = agent["RealName"] \
457                                    if agent.has_key("RealName") \
458                                    else agent["ViewName"]
459        inst_dict["text"] = agent["Text"] \
460                                    if agent.has_key("Text") \
461                                    else None
462        if self.dialogues.has_key(inst_id):
463            inst_dict["dialogue"] = self.dialogues[inst_id]
464        inst_dict["target_map_name"] = agent["TargetMap"] \
465                                        if agent.\
466                                            has_key("TargetMap") \
467                                        else None
468        inst_dict["target_x"] = agent["TargetPosition"][0] \
469                                    if agent.\
470                                        has_key("TargetPosition") \
471                                    else None
472        inst_dict["target_y"] = agent["TargetPosition"][1] \
473                                    if agent.\
474                                        has_key("TargetPosition") \
475                                    else None
476        if agent.has_key("Inventory"):
477            inventory = Inventory()
478            inventory_objs = agent["Inventory"]
479            for inventory_obj in inventory_objs:
480                self.createInventoryObject(inventory,
481                                           inventory_obj
482                                           )
483            inst_dict["inventory"] = inventory
484
485        if agent.has_key("Items"):
486            items = []
487            inventory_objs = agent["Items"]
488            for inventory_obj in inventory_objs:
489                items.append(self.createContainerObject(inventory_obj))
490            inst_dict["items"] = items
491           
492        if agent.has_key("ItemType"):
493            item_data = {}
494            item_data["object_type"] = agent["ItemType"]
495            item_data["ID"] = inst_id
496            inst_dict["item"] = item_data
497            inst_dict["item_id"] = agent["ItemType"]
498
499        self.createMapObject(self.active_map.agent_layer, inst_dict, inst)
500   
501    def placeAgents(self):
502        """Places the current maps agents """
503        if not self.active_map:
504            return
505        agents = self.getAgentsOfMap(self.game_state.current_map_name)
506        for agent in agents:
507            if agent == "PlayerCharacter":
508                continue
509            if self.active_map.agent_layer.getInstances(agent):
510                continue
511            self.createAgent(agents[agent], agent)
512
513    def placePC(self):
514        """Places the PlayerCharacter on the map"""
515        agent = self.agents[self.ALL_AGENTS_KEY]["PlayerCharacter"]
516        inst_id = "PlayerCharacter"
517        self.createAgent(agent, inst_id)
518       
519        # create the PlayerCharacter agent
520        self.active_map.addPC()
521        self.game_state.player_character.start()
522        if agent.has_key("PeopleKnown"):
523            self.game_state.player_character.people_i_know = agent["PeopleKnown"]
524                     
525    def changeMap(self, map_name, target_position = None):
526        """Registers for a map change on the next pump().
527           @type map_name: String
528           @param map_name: Id of the map to teleport to
529           @type map_file: String
530           @param map_file: Filename of the map to teleport to
531           @type target_position: Tuple
532           @param target_position: Position of PlayerCharacter on target map.
533           @return None"""
534        # set the parameters for the map change if moving to a new map
535        if map_name != self.game_state.current_map_name:
536            self.target_map_name = map_name
537            self.target_position = target_position
538            # issue the map change
539            self.map_change = True
540
541    def deleteMaps(self):
542        """Clear all currently loaded maps from FIFE as well as clear our
543            local map cache
544            @return: nothing"""
545        self.engine.getModel().deleteMaps()
546        self.engine.getModel().deleteObjects()
547        self.game_state.clearObjects()
548        self.game_state.maps = {}
549       
550    def setActiveMap(self, map_name):
551        """Sets the active map that is to be rendered.
552           @type map_name: String
553           @param map_name: The name of the map to load
554           @return: None"""
555        # Turn off the camera on the old map before we turn on the camera
556        # on the new map.
557        self.active_map.cameras[self.active_map.my_cam_id].setEnabled(False)
558        # Make the new map active.
559        self.active_map = self.game_state.maps[map_name]
560        self.active_map.makeActive()
561        self.game_state.current_map_name = map_name
562
563    def createMapObject (self, layer, attributes, instance):
564        """Create an object and add it to the current map.
565           @type layer: fife.Layer
566           @param layer: FIFE layer object exists in
567           @type attributes: Dictionary
568           @param attributes: Dictionary of all object attributes
569           @type instance: fife.Instance
570           @param instance: FIFE instance corresponding to the object
571           @return: None"""
572        # create the extra data
573        extra = {}
574        if layer is not None:
575            extra['agent_layer'] = layer
576        attributes = self.checkAttributes(attributes)
577       
578        obj = createObject(attributes, extra)
579       
580        if obj.trueAttr("PC"):
581            self.addPC(layer, obj, instance)
582        else:
583            self.addObject(layer, obj, instance) 
584
585    def addPC(self, layer, player_char, instance):
586        """Add the PlayerCharacter to the map
587           @type layer: fife.Layer
588           @param layer: FIFE layer object exists in
589           @type player_char: PlayerCharacter
590           @param player_char: PlayerCharacter object
591           @type instance: fife.Instance
592           @param instance: FIFE instance of PlayerCharacter
593           @return: None"""
594        # For now we copy the PlayerCharacter,
595        # in the future we will need to copy
596        # PlayerCharacter specifics between the different PlayerCharacter's
597        self.game_state.player_character = player_char
598        self.game_state.player_character.setup()       
599
600    def addObject(self, layer, obj, instance):
601        """Adds an object to the map.
602           @type layer: fife.Layer
603           @param layer: FIFE layer object exists in
604           @type obj: GameObject
605           @param obj: corresponding object class
606           @type instance: fife.Instance
607           @param instance: FIFE instance of object
608           @return: None"""
609        ref = self.game_state.getObjectById(obj.ID, \
610                                            self.game_state.current_map_name) 
611        if ref is None:
612            # no, add it to the game state
613            self.game_state.addObject(self.game_state.current_map_name, obj)
614        else:
615            # yes, use the current game state data
616            obj.X = ref.X
617            obj.Y = ref.Y
618            obj.gfx = ref.gfx 
619             
620        if obj.trueAttr("NPC"):
621            # create the agent
622            obj.setup()
623            # create the PlayerCharacter agent
624            obj.start()
625        if obj.trueAttr("AnimatedContainer"):
626            # create the agent
627            obj.setup()
628
629    def objectActive(self, ident):
630        """Given the objects ID, pass back the object if it is active,
631           False if it doesn't exist or not displayed
632           @type ident: string
633           @param ident: ID of object
634           @rtype: boolean
635           @return: Status of result (True/False)"""
636        for game_object in \
637           self.game_state.getObjectsFromMap(self.game_state.current_map_name):
638            if (game_object.ID == ident):
639                # we found a match
640                return game_object
641        # no match
642        return False   
643
644    def movePlayer(self, position):
645        """Code called when the player should move to another location
646           @type position: fife.ScreenPoint
647           @param position: Screen position to move to
648           @return: None"""
649        if(self.pc_run == 1):
650            self.game_state.player_character.run(position)
651        else:
652            self.game_state.player_character.walk(position)
653       
654    def teleportAgent(self, agent, position):
655        """Code called when an agent should teleport to another location
656           @type position: fife.ScreenPoint
657           @param position: Screen position to teleport to
658           @return: None"""
659        agent.teleport(position)
660        self.agents[agent.ID]["Position"] = position
661
662    def readObjectDB(self):
663        """Reads the Object Information Database from a file. """
664        database_file = file(self.object_db_file, "r")
665        database = yaml.load_all(database_file)
666        for object_info in database:
667            self.object_db.update(object_info)
668
669    def getAgentImportFiles(self):
670        """Searches the agents directory for import files """
671        files = locateFiles("*.xml", self.agents_directory)
672        for xml_file in files:
673            xml_file = os.path.relpath(xml_file).replace("\\", "/")
674            root = ElementTree.parse(xml_file).getroot()
675            if root.tag == "object":
676                self.agent_import_files[root.attrib["id"]] = xml_file
677   
678    def getDialogues(self):
679        """Searches the dialogue directory for dialogues """
680        files = locateFiles("*.yaml", self.dialogues_directory)
681        for dialogue_file in files:
682            dialogue_file = os.path.relpath(dialogue_file).replace("\\", "/")
683            dialogues = yaml.load_all(file(dialogue_file, "r"))
684            for dialogue in dialogues:
685                self.dialogues[dialogue["NPC"]] = dialogue
Note: See TracBrowser for help on using the repository browser.