Esempio n. 1
0
File: engine.py Progetto: m64/PEG
class Engine:
    """Engine holds the logic for the game.
       Since some data (object position and so forth) is held in the
       fife, and would be pointless to replicate, we hold a instance of
       the fife view here. This also prevents us from just having a
       function heavy controller."""
    
    def __init__(self, view):
        """Initialize the instance.
           @type view: world
           @param view: A world instance
           @return: None"""
        # a World object (the fife stuff, essentially)
        self.view = view
        self.map_change = False
        self.load_saver = False
        self.savegame = None
        self.game_state = GameState()
        self.pc_run = 1
        self.target_position = None
        self.target_map_name = None
        self.target_map_file = None

    def save(self, path, filename):
        """Writes the saver to a file.
           @type filename: string
           @param filename: the name of the file to write to
           @return: None"""
        fname = '/'.join([path, filename])
        try:
            f = open(fname, 'wb')
        except(IOError):
            sys.stderr.write("Error: Can't find save game: " + fname + "\n")
            return
        
        # save the PC coordinates before we destroy the behaviour
        coords = self.game_state.PC.behaviour.agent.getLocation().\
                    getMapCoordinates()
        self.game_state.saved_pc_coordinates = (coords.x, coords.y)
        
        # can't pickle SwigPyObjects
        behaviours = {}
        behaviour_player = self.game_state.PC.behaviour
        self.game_state.PC.behaviour = None
        
        # Backup the behaviours 
        for map_id in self.game_state.objects:
            behaviours[map_id] = {}
            for (object_id, npc) in self.game_state.objects[map_id].items():
                if npc.trueAttr("NPC"):
                    behaviours[map_id][object_id] = npc.behaviour
                    npc.behaviour = None
        
        # Pickle it 
        pickle.dump(self.game_state, f)
        f.close()
        
        # Restore behaviours
        for map_id in behaviours:
            for (object_id, behaviour) in behaviours[map_id].items():
                self.game_state.objects[map_id][object_id].behaviour = \
                    behaviours[map_id][object_id]
                
        self.game_state.PC.behaviour = behaviour_player

    def load(self, path, filename):
        """Loads a saver from a file.
           @type filename: string
           @param filename: the name of the file (including path) to load from
           @return: None"""
            
        fname = '/'.join([path, filename])

        try:
            f = open(fname, 'rb')
        except(IOError):
            sys.stderr.write("Error: Can't find save game file\n")
            return
        
        #Remove all currently loaded maps so we can start fresh
        self.view.deleteMaps();
        
        self.game_state = pickle.load(f)
        f.close()
        
        # Recreate all the behaviours. These can't be saved because FIFE
        # objects cannot be pickled
        for map_id in self.game_state.objects:
            for (object_id, npc) in self.game_state.objects[map_id].items():
                if npc.trueAttr("NPC"):
                    npc.createBehaviour(self.view.active_map.agent_layer)

        # Fix the player behaviour
        self.game_state.PC.createBehaviour(self.view.active_map.agent_layer)
        
        #In most maps we'll create the PC Instance internally. In these
        #cases we need a target position
        self.target_position = self.game_state.saved_pc_coordinates
        
        #Load the current map
        if self.game_state.current_map_file:
            self.loadMap(self.game_state.current_map_name, \
                         self.game_state.current_map_file) 
            
    def createObject (self, layer, attributes, instance):
        """Create an object and add it to the current map.
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type attributes: Dictionary
           @param attributes: Dictionary of all object attributes
           @type instance: fife.Instance
           @param instance: FIFE instance corresponding to the object
           @return: None
        """
        # create the extra data
        extra = {}
        extra['agent_layer'] = layer
        extra['engine'] = self
        
        obj = createObject(attributes, extra)
        
        if obj.trueAttr("PC"):
            self.addPC(layer, obj, instance)
        else:
            self.addObject(layer, obj, instance)

        

    def addPC(self, layer, pc, instance):
        """Add the PC to the map
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type pc: PlayerCharacter
           @param pc: PlayerCharacter object
           @type instance: fife.Instance
           @param instance: FIFE instance of PC
           @return: None
        """
        # For now we copy the PC, in the future we will need to copy
        # PC specifics between the different PC's
        self.game_state.PC = pc
        self.game_state.PC.setup()
        
        # The PC has an inventory, and also some filling of the ready slots 
        # in the HUD. At this point we sync the contents of the ready slots 
        # with the contents of the inventory.
        self.view.hud.initializeInventory()

    def addObject(self, layer, obj, instance):
        """Adds an object to the map.
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type obj: GameObject
           @param obj: corresponding object class
           @type instance: fife.Instance
           @param instance: FIFE instance of object
           @return: None
        """

        ref = self.game_state.getObjectById(obj.ID, \
                                            self.game_state.current_map_name) 
        if ref is None:
            # no, add it to the game state
            self.game_state.objects[self.game_state.current_map_name][obj.ID] \
                                                                          = obj
        else:
            # yes, use the current game state data
            obj.X = ref.X
            obj.Y = ref.Y
            obj.gfx = ref.gfx  
             
        if obj.trueAttr("NPC"):
            # create the agent
            obj.setup()
            
            # create the PC agent
            obj.start()

    def objectActive(self, ident):
        """Given the objects ID, pass back the object if it is active,
           False if it doesn't exist or not displayed
           @type ident: string
           @param ident: ID of object
           @rtype: boolean
           @return: Status of result (True/False)"""
        for i in \
           self.game_state.getObjectsFromMap(self.game_state.current_map_name):
            if (i.ID == ident):
                # we found a match
                return i
        # no match
        return False

    def getItemActions(self, obj_id):
        """Given the objects ID, return the text strings and callbacks.
           @type obj_id: string
           @param obj_id: ID of object
           @rtype: list
           @return: List of text and callbacks"""
        actions = []
        # note: ALWAYS check NPC's first!
        obj = self.game_state.getObjectById(obj_id, \
                                            self.game_state.current_map_name)
        
        if obj is not None:
            if obj.trueAttr("NPC"):
                # keep it simple for now, None to be replaced by callbacks
                actions.append(["Talk", "Talk", self.initTalk, obj])
                actions.append(["Attack", "Attack", self.nullFunc, obj])
            else:
                actions.append(["Examine", "Examine", \
                                self.game_state.PC.approach, [obj.X, obj.Y], \
                                ExamineBoxAction(self, obj.name, obj.text)])
                # is it a Door?
                if obj.trueAttr("door"):
                    actions.append(["Change Map", "Change Map", \
                       self.game_state.PC.approach, [obj.X, obj.Y], \
                            ChangeMapAction(self, obj.target_map_name, \
                                obj.target_map, obj.target_pos)])
                # is it a container?
                if obj.trueAttr("container"):
                    actions.append(["Open", "Open", 
                                    self.game_state.PC.approach, \
                                    [obj.X, obj.Y], \
                                    OpenBoxAction(self, obj)])
                    actions.append(["Unlock", "Unlock", 
                                    self.game_state.PC.approach, \
                                    [obj.X, obj.Y], \
                                    UnlockBoxAction(obj)])
                    actions.append(["Lock", "Lock", 
                                    self.game_state.PC.approach, \
                                    [obj.X, obj.Y], \
                                    LockBoxAction(obj)])
                # can you pick it up?
                if obj.trueAttr("carryable"):
                    actions.append(["Pick Up", "Pick Up", self.nullFunc, obj])

        return actions
    
    def nullFunc(self, userdata):
        """Sample callback for the context menus."""
        print userdata
    
    def initTalk(self, npcInfo):
        """ Starts the PC talking to an NPC. """
        # TODO: work more on this when we get NPCData and HeroData straightened
        # out
        npc = self.game_state.getObjectById(npcInfo.ID, \
                                            self.game_state.current_map_name)
        self.game_state.PC.approach([npc.getLocation().\
                                     getLayerCoordinates().x, \
                                     npc.getLocation().\
                                     getLayerCoordinates().y], \
                                    TalkAction(self, npc))

    def loadMap(self, map_name, map_file):
        """Load a new map.
           @type map_name: string
           @param map_name: Name of the map to load
           @type map_file: string
           @param map_file: Filename of map file to load
           @return: None"""
        self.game_state.current_map_file = map_file
        self.game_state.current_map_name = map_name
        self.view.loadMap(map_name, str(map_file))

        # create the PC agent
        self.view.active_map.addPC()
        self.game_state.PC.start()

    def handleMouseClick(self, position):
        """Code called when user left clicks the screen.
           @type position: fife.ScreenPoint
           @param position: Screen position of click
           @return: None"""
        if(self.pc_run == 1):
            self.game_state.PC.run(position)
        else:
            self.game_state.PC.walk(position)

    def changeMap(self, map_name, map_file, target_position):
        """Registers for a map change on the next pump().
           @type name_name: String
           @param map_name: Id of the map to teleport to
           @type map_file: String
           @param map_file: Filename of the map to teleport to
           @type target_position: Tuple
           @param target_position: Position of PC on target map.
           @return None"""
        # set the parameters for the map change if moving to a new map
        if map_name != self.game_state.current_map_name:
            self.target_map_name = map_name
            self.target_map_file = map_file
            self.target_position = target_position
            # issue the map change
            self.map_change = True
        else:
            #set the player position on the current map
            self.view.teleport(target_position)

    def handleCommands(self):
        if self.map_change:
            self.loadMap(self.target_map_name, self.target_map_file)
            self.view.teleport(self.target_position)
            self.map_change = False
        
        if self.load_saver:
            self.load(self.savegame)
            self.load_saver = False

    def pump(self):
        """Main loop in the engine."""
        self.handleCommands()
Esempio n. 2
0
class Engine:
    """Engine holds the logic for the game.
       Since some data (object position and so forth) is held in the
       fife, and would be pointless to replicate, we hold a instance of
       the fife view here. This also prevents us from just having a
       function heavy controller."""
    
    def __init__(self, view):
        """Initialise the instance.
           @type view: world
           @param view: A world instance
           @return: None"""
        # a World object (the fife stuff, essentially)
        self.view = view
        self.mapchange = False
        self.gameState = GameState()

    def reset(self):
        """Clears the data on a map reload so we don't have objects/npcs from
           other maps hanging around.
           @return: None"""

    def save(self, path, filename):
        """Writes the saver to a file.
           @type filename: string
           @param filename: the name of the file to write to
           @return: None"""
        fname = '/'.join([path,filename])
        try:
            f = open(fname, 'w')
        except(IOError):
            sys.stderr.write("Error: Can't find save game: " + fname + "\n")
            return
        
        # can't pickle SwigPyObjects
        behaviours = {}
        behaviours[self.gameState.PC.ID] = self.gameState.PC.behaviour;
        self.gameState.PC.behaviour = None;
        
        npcs = [npc for npc in self.gameState.objects.values() if npc.trueAttr("NPC")]
        for npc in npcs:
            behaviours[npc.ID] = npc.behaviour;
            npc.behaviour = None;
        
        pickle.dump(self.gameState, f)
        f.close()
        
        # restore behaviours
        for npc in npcs:
            npc.behaviour = behaviours[npc.ID];
        self.gameState.PC.behaviour = behaviours[self.gameState.PC.ID]

    def load(self, path, filename):
        """Loads a saver from a file.
           @type filename: string
           @param filename: the name of the file to load from
           @return: None"""
        fname = '/'.join([path, filename])
        try:
            f = open(fname, 'r')
        except(IOError):
            sys.stderr.write("Error: Can't find save game file\n")
            return
        self.gameState = pickle.load(f)
        f.close()
        if self.gameState.currentMap:
            self.loadMap(self.gameState.currentMap) 

    def createObject (self, layer, attributes, instance):
        """Create an object and add it to the current map.
            Inputs:
                layer = FIFE layer object exists in
                attributes = dictionary of all object attributes
                instance = FIFE instance corresponding to the object
            Return:
                Nothing
        """
        # create the extra data
        extra = {}
        extra['agent_layer'] = layer
        extra['engine'] = self
        
        obj = createObject(attributes, extra)
        
        if obj.trueAttr("PC"):
            self.addPC( layer, obj, instance)
        else:
            self.addObject( layer, obj, instance)

        

    def addPC(self, layer, pc, instance):
        """Add the PC to the map
            Inputs:
                layer = FIFE layer object exists in
                pc = PlayerCharacter object
                instance = FIFE instance of PC
            Returns:
                Nothing
        """
        # add to view data 
        self.view.activeMap.addObject(pc.ID, instance)          
        
        # sync with game data
        if not self.gameState.PC:
            self.gameState.PC = pc
            
        self.gameState.PC.setup()


    def addObject(self, layer, obj, instance):
        """Adds an object to the map.
            Inputs:
                layer = FIFE layer object exists in
                obj = corresponding object class
                instance = FIFE instance of object
            Returns:
                Nothing
        """
        
        ref = self.gameState.getObjectById(obj.ID) 
        if ref is None:
            # no, add it to the game state
            obj.map_id = self.gameState.currentMap
            self.gameState.objects[obj.ID] = obj
        else:
            # yes, use the current game state data
            obj.X = ref.X
            obj.Y = ref.Y
            obj.gfx = ref.gfx  
            
        # add it to the view
        self.view.activeMap.addObject(obj.ID, instance)          
       
        if obj.trueAttr("NPC"):
            # create the agent
            obj.setup()
            
            # create the PC agent
            obj.start()

    def addDoors(self, doors):
        """Add all the doors to the map as well.
           As an object they will have already been added.
           @type doors: list
           @param doors: List of doors
           @return: None"""
        for i in doors:
            self.doors[str(i.id)] = MapDoor(i.id, i.destmap, (i.destx, i.desty))

    def objectActive(self, ident):
        """Given the objects ID, pass back the object if it is active,
           False if it doesn't exist or not displayed
           @type ident: string
           @param ident: ID of object
           @rtype: boolean
           @return: Status of result (True/False)"""
        for i in self.gameState.getObjectsFromMap(self.gameState.currentMap):
            if (i.ID == ident):
                # we found a match
                return i         
        # no match
        return False

    def getItemActions(self, obj_id):
        """Given the objects ID, return the text strings and callbacks.
           @type obj_id: string
           @param obj_id: ID of object
           @rtype: list
           @return: List of text and callbacks"""
        actions=[]
        # note: ALWAYS check NPC's first!
        obj = self.gameState.getObjectById(obj_id)
        
        if obj:
            if obj.trueAttr("NPC"):
                # keep it simple for now, None to be replaced by callbacks
                actions.append(["Talk", "Talk", self.initTalk, obj])
                actions.append(["Attack", "Attack", self.nullFunc, obj]) 
            elif obj.trueAttr("Door"):
                actions.append(["Change Map", "Change Map", \
                       self.gameState.PC.approach, [obj.X, obj.Y], \
                        ChangeMapAction(self, self.doors[str(i.ID)].map, [i.destx, i.desty])])
                pass
            else:
                actions.append(["Examine", "Examine", self.gameState.PC.approach,  
                                [obj.X, obj.Y], ExamineBoxAction(self, obj.name, obj.text)])
                # is it a container?
                if obj.trueAttr("container"):
                    actions.append(["Open", "Open", self.gameState.PC.approach, [obj.X, obj.Y], OpenBoxAction(self, "Box")])
                # can you pick it up?
                if obj.trueAttr("carryable"):
                    actions.append(["Pick Up", "Pick Up", self.nullFunc, obj])       
                    
        return actions
    
    def nullFunc(self, userdata):
        """Sample callback for the context menus."""
        print userdata
    
    def initTalk(self, npcInfo):
        """ Starts the PC talking to an NPC. """
        # TODO: work more on this when we get NPCData and HeroData straightened
        # out
        npc = self.gameState.getObjectById(npcInfo.ID)
        if npc:
            npc.talk()
        self.gameState.PC.approach([npc.getLocation().getLayerCoordinates().x, npc.getLocation().getLayerCoordinates().y])

    def loadMap(self, map_name, map_file):
        """Load a new map. TODO: needs some error checking
           @type map_file: string
           @param map_file: Name of map file to load
           @return: None"""
        self.gameState.currentMap = map_file
        self.view.loadMap(map_name, str(map_file))
        self.view.setActiveMap(map_name)

        self.reset()        
        
        # create the PC agent
        self.view.activeMap.addPC(self.gameState.PC.behaviour.agent)
        self.gameState.PC.start()

    def handleMouseClick(self,position):
        """Code called when user left clicks the screen.
           @type position: fife.ScreenPoint
           @param position: Screen position of click
           @return: None"""
        self.gameState.PC.run(position)
        
    def changeMap(self, map, targetPosition):
        """Registers for a mapchange on the next pump().
           @type map: ???
           @param map: Name of the target map.
           @type targetPosition: ???
           @param targetPosition: Position of PC on target map.
           @return: None"""
        # save the postions
        self.updateGameState()
        # set the PC position
        self.gameState.PC.posx = targetPosition[0]
        self.gameState.PC.posy = targetPosition[1]
        # set the parameters for the mapchange
        self.targetMap = map
        # issue the mapchange
        self.mapchange = True

    def handleCommands(self):
        if self.mapchange:
            self.loadMap(self.targetMap)
            self.mapchange = False

    def pump(self):
        """Main loop in the engine."""
        self.handleCommands()
Esempio n. 3
0
class GameModel(object):
    """GameModel holds the logic for the game.
       Since some data (object position and so forth) is held in the
       fife, and would be pointless to replicate, we hold a instance of
       the fife view here. This also prevents us from just having a
       function heavy controller."""
    ALL_AGENTS_KEY = "All"
    MAX_ID_NUMBER = 1000
    GENERIC_ITEM_GFX = "generic_item"
    DEFAULT_STAT_VALUE = 50
    
    def __init__(self, engine, settings):
        """Initialize the instance.
        @param engine: A fife.Engine object
        @type emgome: fife.Engine 
        @param setting: The applications settings
        @type setting: parpg.settings.Settings object
        @return: None"""
        self.settings = settings

        self.map_change = False
        self.load_saver = False
        self.savegame = None
        quests_directory = settings.get("parpg", "QuestsPath")
        #setup functions for the GameEnvironment        
        self.game_state = GameState(quests_dir=quests_directory)
        funcs = {
                 "moveObject":self.moveObject, 
                 "deleteObject":self.deleteObject, 
                 "putItemIntoContainer":container.put_item,
                 "equipItem":equip.equip, 
                 }
        self.game_state.funcs.update(funcs)
        self.pc_run = 1
        self.target_position = None
        self.target_map_name = None
        self.object_db = {}
        self.active_map = None
        self.map_files = {}
        self.agents = {}
        self.agents[self.ALL_AGENTS_KEY] = {}
        self.items = {}
        self.engine = engine
        self.fife_model = engine.getModel()

        # set values from settings
        maps_directory = settings.get("parpg", "MapsPath")
        self.game_state.maps_file = '/'.join([maps_directory,
                                              settings.get("parpg", "MapsFile")])
        self.all_agents_file = '/'.join([maps_directory,
                                         settings.get("parpg", "AllAgentsFile")])
        objects_directory = self.settings.get("parpg", "ObjectsPath")
        self.objects_directory = objects_directory
        self.object_db_file = '/'.join([objects_directory,
                                        settings.get("parpg", "ObjectDatabaseFile")])
        self.dialogue_directory = settings.get("parpg", "DialoguesPath")
        self.dialogues = {}
        self.agent_import_files = {}
        self.obj_loader = XMLObjectLoader(self.engine)
        # FIXME M. George Hansen 2011-06-06: character stats scripts aren't
        #     finished, unfortunately.
        # NOTE Beliar 2011-11-05 Activated the stats. Testing needed if it 
        # works correctly, or if they are still unfinished.
        primary_stats_file = (
            vfs.VFS.open('character_scripts/primary_stats.xml')
        )
        self.primary_stats = XmlSerializer.deserialize(primary_stats_file)
        secondary_stats_file = (
            vfs.VFS.open('character_scripts/secondary_stats.xml')
        )
        self.secondary_stats = XmlSerializer.deserialize(secondary_stats_file)        

        
    def create_stats(self, entity):
        for primary_stat in self.primary_stats:
            long_name = primary_stat.long_name
            entity.characterstats.primary_stats[long_name] = (
                char_stats.PrimaryStatisticValue(
                    primary_stat, entity.characterstats, 
                    self.DEFAULT_STAT_VALUE)
            )
        for secondary_stat in self.secondary_stats:
            name = secondary_stat.name            
            entity.characterstats.secondary_stats[name] = (
                char_stats.SecondaryStatisticValue(secondary_stat, 
                                                   entity.characterstats
                                                   )
            )
            
    def checkAttributes(self, attributes, template):
        """Checks for attributes that where not given in the map file
        and fills them with values from the object database
        @param attributes: attributes to check        
        @type attributes: Dictionary
        @param template: Template from which the values will be used
        @return: The modified attributes""" 
        if self.object_db.has_key(template):
            db_attributes = deepcopy(self.object_db[template])
            for key in db_attributes.keys():
                if attributes.has_key(key):                    
                    tmp_attributes = db_attributes[key]
                    tmp_attributes.update(attributes[key])
                    attributes[key] = tmp_attributes
                else:
                    attributes[key] = db_attributes[key]
        return attributes
    
    def isIDUsed(self, ID):
        if self.game_state.hasObject(ID):
            return True
        for namespace in self.agents:
            if ID in self.agents[namespace]:
                return True
        return False
    
    def createUniqueID(self, ID):
        if self.isIDUsed(ID):
            id_number = 1
            while self.isIDUsed(ID + "_" + str(id_number)):
                id_number += 1
                if id_number > self.MAX_ID_NUMBER:
                    raise ValueError(
                        "Number exceeds MAX_ID_NUMBER:" + 
                        str(self.MAX_ID_NUMBER)
                    )
            
            ID = ID + "_" + str(id_number)
        return ID
    
    def moveObject(self, object_id, new_map):
        """Moves the object to a new map, or in a container
        @param object_id: ID of the object
        @type object_id: str 
        @param new_map: ID of the new map, or None
        @type object_id: str """
        game_object = self.deleteObject(object_id)
        self.game_state.addObject(object_id, new_map, game_object)

    def deleteObject(self, object_id):
        """Removes an object from the game
        @param object_id: ID of the object
        @type object_id: str """
        if self.agents["All"].has_key(object_id):
            del self.agents["All"][object_id]
        else:
            del self.items[object_id]
        return self.game_state.deleteObject(object_id)
        
    def save(self, path, filename):
        """Writes the saver to a file.
           @type filename: string
           @param filename: the name of the file to write to
           @return: None"""
        fname = '/'.join([path, filename])
        try:
            save_file = open(fname, 'w')
        except(IOError):
            sys.stderr.write("Error: Can't create save game: " + fname + "\n")
            return

        save_state = {}
        save_state["Agents"] = self.agents
        save_state["Items"] = self.items
        save_state["GameState"] = self.game_state.getStateForSaving()
        
        yaml.dump(save_state, save_file)
        
        save_file.close()       

    def load(self, path, filename):
        """Loads a saver from a file.
           @type filename: string
           @param filename: the name of the file (including path) to load from
           @return: None"""
        fname = os.path.join(path, filename)

        try:
            load_file = open(fname, 'r')
        except(IOError):
            sys.stderr.write("Error: Can't find save game file '" + fname + "'\n")
            return        
        self.deleteMaps()
        self.clearAgents()
        
        save_state = yaml.load(load_file)
        self.game_state.restoreFromState(save_state["GameState"])
        maps = save_state["Agents"]
        for map_name in maps:
            for agent_name in maps[map_name]:
                agent = {agent_name:maps[map_name][agent_name]}
                self.addAgent(map_name, agent)
        self.items = save_state["Items"]               
      
        load_file.close()             
         
    def teleport(self, agent, position):
        """Called when a an agent is moved instantly to a new position. 
        The setting of position may wan to be created as its own method down 
        the road.
        @type position: String Tuple
        @param position: X,Y coordinates passed from engine.changeMap
        @return: fife.Location"""
        logging.debug(position)
        coord = fife.DoublePoint3D(float(position[0]), float(position[1]), 0)
        location = fife.Location(self.active_map.agent_layer)
        location.setMapCoordinates(coord)
        agent.teleport(location)         
               
    def getObjectAtCoords(self, coords):
        """Get the object which is at the given coords
        @type coords: fife.Screenpoint
        @param coords: Coordinates where to check for an object
        @rtype: fife.Object
        @return: An object or None"""
        instances = self.active_map.cameras[
                                            self.active_map.my_cam_id].\
            getMatchingInstances(coords, self.active_map.agent_layer)
        # no object returns an empty tuple
        if(instances != ()):
            front_y = 0
            

            for obj in instances:
                # check to see if this in our list at all
                if(self.objectActive(obj.getId())):
                    # check if the object is on the foreground
                    obj_map_coords = \
                                      obj.getLocation().getMapCoordinates()
                    obj_screen_coords = self.active_map.\
                        cameras[self.active_map.my_cam_id]\
                        .toScreenCoordinates(obj_map_coords)

                    if obj_screen_coords.y > front_y:
                        #Object on the foreground
                        front_y = obj_screen_coords.y
                        return obj
                    else:
                        return None
        else:
            return None

    def getCoords(self, click):
        """Get the map location x, y coordinates from the screen coordinates
           @type click: fife.ScreenPoint
           @param click: Screen coordinates
           @rtype: fife.Location
           @return: The map coordinates"""
        coord = self.active_map.cameras[self.active_map.my_cam_id].\
                    toMapCoordinates(click, False)
        coord.z = 0
        location = fife.Location(self.active_map.agent_layer)
        location.setMapCoordinates(coord)
        return location

    def pause(self, paused):
        """ Pause/Unpause the game
        @return: nothing"""
        if self.active_map:
            self.active_map.pause(paused)
    
    def togglePause(self):
        """ Toggle paused state.
        @return: nothing"""
        self.active_map.togglePause()
        
    def isPaused(self):
        """Returns wheter the game is paused or not"""
        return self.active_map.isPaused()
    
    def readMapFiles(self):
        """Read all a available map-files and store them"""
        maps_file = vfs.VFS.open(self.game_state.maps_file)
        self.map_files = yaml.load(maps_file)["Maps"]
    
    def addAgent(self, namespace, agent):
        """Adds an agent to the agents dictionary
        @param namespace: the namespace where the agent is to be added to
        @type namespace: str
        @param agent: The agent to be added
        @type agent: dict """
        from fife.extensions.serializers.xml_loader_tools import loadImportFile
        if not self.agents.has_key(namespace):
            self.agents[namespace] = {}
            
        agent_values = agent.values()[0]
        unique_agent_id = self.createUniqueID(agent.keys()[0])
        del agent[agent.keys()[0]]
        agent[unique_agent_id] = agent_values
        self.agents[namespace].update(agent)
        object_model = ""
        if agent_values["Entity"].has_key("graphics") \
           and agent_values["Entity"]["graphics"].has_key("gfx"): 
            object_model = agent_values["Entity"]["graphics"]["gfx"]
        elif agent_values.has_key("Template"):
            template = self.object_db[agent_values["Template"]]
            object_model = template["graphics"]["gfx"]
        else:
            object_model = self.GENERIC_ITEM_GFX
        import_file = self.agent_import_files[object_model]
        loadImportFile(self.obj_loader, import_file, self.engine)
        
    def readAgentsOfMap(self, map_name):
        """Read the agents of the map
        @param map_name: Name of the map
        @type map_name: str """
        #Get the agents of the map
        map_agents_file = self.map_files[map_name].\
                            replace(".xml", "_agents.yaml")   
        agents_data = vfs.VFS.open(map_agents_file)
        agents = yaml.load_all(agents_data)
        self.agents[map_name] = {}
        for agent in agents:
            if not agent == None:
                self.addAgent(map_name, agent)  
        
    def readScriptsOfMap(self, map_name, world):
        """Read the scripts of the map
        @param map_name: Name of the map
        @type map_name: str 
        @param world: The current active world
        @type world: parpg.world.World"""
        map_scripts_file = (
            self.map_files[map_name].replace(".xml", "_scripts.yaml")
        )
        if vfs.VFS.exists(map_scripts_file):
            scripts_file = vfs.VFS.open(map_scripts_file)
            scripts_data = yaml.load(scripts_file)
            scripts = (scripts_data["Scripts"])
            conditions = (
                scripts_data["Conditions"] if 
                scripts_data.has_key("Conditions") else ()
            )
            scripting = world.systems.scripting
            for name, actions in scripts.iteritems():
                scripting.setScript(name, actions)
            for condition in conditions:
                scripting.addCondition(*condition)            
            
    def readAllAgents(self):
        """Read the agents of the all_agents_file and store them"""
        agents_file = vfs.VFS.open(self.all_agents_file)
        agents = yaml.load_all(agents_file)
        for agent in agents:
            if agent is not None:
                self.addAgent(self.ALL_AGENTS_KEY, agent)  
                
    def getAgentsOfMap(self, map_name):
        """Returns the agents that are on the given map
        @param map_name: Name of the map
        @type map_name: str
        @return: A dictionary with the agents of the map"""
        if not self.agents.has_key(map_name):
            return {}
        ret_dict = self.agents[map_name].copy()
        for agent_name, agent_value in self.agents[self.ALL_AGENTS_KEY]\
                                                .iteritems():
            if agent_value["Map"] == map_name:
                ret_dict[agent_name] = agent_value
        return ret_dict
                
    def getAgentsOfActiveMap(self):
        """Returns the agents that are on active map
        @return: A dictionary with the agents of the map """
        return self.getAgentsOfMap(self.active_map.map.getId())

    def clearAgents(self):
        """Resets the agents dictionary"""
        self.agents = {}
        self.agents[self.ALL_AGENTS_KEY] = {}
    
    def loadMap(self, map_name):
        """Load a new map.
           @type map_name: string
           @param map_name: Name of the map to load
           @return: None"""
        if not map_name in self.game_state.maps:  
            map_file = self.map_files[map_name]
            new_map = GameMap(self.engine, self)
            self.game_state.maps[map_name] = new_map
            new_map.load(map_file)    

    def createAgent(self, agent, inst_id, world):
        if self.game_state.hasObject(inst_id):
            return None
        entity_data = deepcopy(agent["Entity"])
        entity_data["fifeagent"] = {}
        template = None
        if agent.has_key("Template"):
            template = agent["Template"]
            entity_data = self.checkAttributes(entity_data, template)
        object_id = (entity_data["graphics"]["gfx"] 
                     if entity_data.has_key("graphics") and 
                     entity_data["graphics"].has_key("gfx") 
                     else self.GENERIC_ITEM_GFX
                     )
        map_obj = self.fife_model.getObject(str(object_id), "PARPG")
        if not map_obj:
            logging.warning("Object with inst_id={0}, ns=PARPG, "
                                  "could not be found. "
                                  "Omitting...".format(str(object_id)))

        x_pos = agent["Position"][0]
        y_pos = agent["Position"][1]
        z_pos = agent["Position"][2] if len(agent["Position"]) == 3 \
                                        else 0.0  
        stack_pos = agent["Stackposition"] if \
                        agent.has_key("StackPosition") \
                        else None
        inst = self.active_map.agent_layer.\
                        createInstance(map_obj,
                                       fife.ExactModelCoordinate(x_pos, 
                                                                 y_pos, 
                                                                 z_pos),
                                       inst_id)
        inst.setId(inst_id)

        rotation = agent["Rotation"]
        inst.setRotation(rotation)

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

        if (map_obj.getAction('default')):
            target = fife.Location(self.active_map.agent_layer)
            inst.act('default', target, True)        

        if entity_data.has_key("behaviour"):
            entity_data["fifeagent"]["behaviour"] = \
                getattr(behaviours, 
                        entity_data["behaviour"]["behaviour_type"])()
        else:
            entity_data["fifeagent"]["behaviour"] = behaviours.Base()
        if self.dialogues.has_key(inst_id):
            entity_data["dialogue"] = {}
            entity_data["dialogue"]["dialogue"] = self.dialogues[inst_id]
        if (entity_data.has_key("containable") and not 
            entity_data["containable"].has_key("item_type")
            ):
            entity_data["containable"]["item_type"] = template          
                      
        obj = self.createMapObject(self.active_map.agent_layer, 
                                   entity_data, inst_id, world)

        if agent.has_key("Statistics"):
            self.create_stats(obj)
            for name, val in agent["Statistics"].iteritems():
                obj.characterstats.primary_stats[name].value = val

        if agent.has_key("Inventory"):
            inv = agent["Inventory"]
            self.createInventoryItems(inv, obj, world)

        if agent.has_key("Equipment"):
            for slot, data in agent["Equipment"].iteritems():
                item = None
                if data.has_key("type"):
                    item_type = data["type"]
                    item_data = {}
                    item_data = self.checkAttributes(item_data, item_type)
                    if (item_data.has_key("containable") and 
                        item_data.has_key("equipable")):
                        item = self.createItem(
                            self.createUniqueID(data["ID"]), 
                            item_data, world, item_type)
                    else:
                        raise Exception(
                            "Item %s is not containable or equipable." % 
                            item_type
                        )
                else:
                    identifier = data["ID"]
                    if self.game_state.hasObject(identifier):
                        item = self.game_state.getObjectById(identifier)
                    else:
                        item_data = self.items[identifier]["Entity"]
                        item_type = item_data["containable"]["item_type"]
                        item = self.createItem(identifier, item_data,
                                                world, item_type)
                equip.equip(obj.equip, item.equipable, slot)
        if (obj.fifeagent and (obj.lockable and not obj.lockable.closed)):
            obj.fifeagent.behaviour.animate("opened", repeating=True)
        return obj

    def createInventoryItems(self, inv, obj, world):
        slots = inv["Slots"]
        obj.container.children = list()
        for x in xrange(slots):
            obj.container.children.append(None)
        items = inv["Items"] if inv.has_key("Items") else list()
        for data in items:
            item = None
            slot = data["Slot"] if data.has_key("Slot") else -1
            if data.has_key("type"):
                item_type = data["type"]
                item = self.createItemByType(item_type, data["ID"], world)
            else:
                identifier = data["ID"]
                item = self.createItemByID(world, identifier)
                    
            container.put_item(obj.container, item.containable, slot)

    def createItemByID(self, world, identifier):
        if self.game_state.hasObject(identifier):
            item = self.game_state.getObjectById(identifier)
        else:
            agent_data = self.items[identifier]
            item_data = agent_data["Entity"]
            item_type = item_data["containable"]["item_type"]
            item = self.createItem(identifier, item_data,
                                    world, item_type)
            if item.container and agent_data.has_key("Inventory"):
                self.createInventoryItems(agent_data["Inventory"],
                                            item, world)
        return item

    def createItemByType(self, item_type, identifier, world):
        item_data = {}
        item_data = self.checkAttributes(item_data, item_type)
        if item_data.has_key("containable"):
            return self.createItem( self.createUniqueID(identifier), 
                                    item_data, world, item_type)
        else:
            raise Exception("Item %s is not containable." % item_type)

    def createItem(self, identifier, item_data, world, item_type):
        if not item_data["description"].has_key("view_name"):
            item_data["description"]["view_name"] = (
             item_data["description"]["real_name"])
        item = createEntity(item_data, identifier, world, None)
        item.containable.item_type = item_type
        self.game_state.addObject(identifier, None, item)
        self.updateObjectDB(world)
        return item

    def placeAgents(self, world):
        """Places the current maps agents """
        if not self.active_map:
            return
        agents = self.getAgentsOfMap(self.game_state.current_map_name)
        for agent in agents:
            if agent == "PlayerCharacter":
                continue
            if self.active_map.agent_layer.getInstances(agent):
                continue
            self.createAgent(agents[agent], agent, world)

    def placePC(self, world):
        """Places the PlayerCharacter on the map"""
        agent = self.agents[self.ALL_AGENTS_KEY]["PlayerCharacter"]
        inst_id = "PlayerCharacter"
        self.createAgent(agent, inst_id, world)
        
        # create the PlayerCharacter agent
        self.active_map.addPC()
        #self.game_state.getObjectById("PlayerCharacter").fifeagent.start()
        if agent.has_key("PeopleKnown"):
            player = self.game_state.getObjectById("PlayerCharacter")
            player.fifeagent.people_i_know = agent["PeopleKnown"]
                      
    def changeMap(self, map_name, target_position = None):
        """Registers for a map change on the next pump().
           @type map_name: String
           @param map_name: Id of the map to teleport to
           @type map_file: String
           @param map_file: Filename of the map to teleport to
           @type target_position: Tuple
           @param target_position: Position of PlayerCharacter on target map.
           @return None"""
        # set the parameters for the map change if moving to a new map
        if map_name != self.game_state.current_map_name:
            self.target_map_name = map_name
            self.target_position = target_position
            # issue the map change
            self.map_change = True

    def deleteMaps(self):
        """Clear all currently loaded maps from FIFE as well as clear our
            local map cache
            @return: nothing"""
        self.engine.getModel().deleteMaps()
        self.engine.getModel().deleteObjects()
        self.game_state.clearObjects()
        self.game_state.maps = {}
        
    def setActiveMap(self, map_name, world):
        """Sets the active map that is to be rendered.
           @type map_name: String
           @param map_name: The name of the map to load
           @param world: The active world
           @type world: parpg.world.World
           @return: None"""
        # Turn off the camera on the old map before we turn on the camera
        # on the new map.
        self.active_map.cameras[self.active_map.my_cam_id].setEnabled(False)
        # Make the new map active.
        self.active_map = self.game_state.maps[map_name]
        self.active_map.makeActive()
        self.game_state.current_map_name = map_name
        if not self.agents.has_key(map_name):
            self.readAgentsOfMap(map_name)

    def createMapObject (self, layer, attributes, inst_id, world):
        """Create an object and add it to the current map.
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type attributes: Dictionary
           @param attributes: Dictionary of all object attributes
           @type instance: fife.Instance
           @param instance: FIFE instance corresponding to the object
           @return: The created object"""
        # create the extra data
        extra = {}
        if layer is not None:
            extra['fifeagent'] = {}
            extra['fifeagent']['layer'] = layer
        
        obj = createEntity(attributes, inst_id, world, extra)
        if obj:
            self.addObject(layer, obj)
        return obj

    def addPC(self, layer, player_char):
        """Add the PlayerCharacter to the map
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type player_char: PlayerCharacter
           @param player_char: PlayerCharacter object
           @type instance: fife.Instance
           @param instance: FIFE instance of PlayerCharacter
           @return: None"""
        # For now we copy the PlayerCharacter, 
        # in the future we will need to copy
        # PlayerCharacter specifics between the different PlayerCharacter's
        player = self.game_state.getObjectById("PlayerCharacter")
        player.fifeagent = player_char
        player.fifeagent.setup()        
        player.fifeagent.behaviour.speed = self.settings.parpg.PCSpeed


    def addObject(self, layer, obj):
        """Adds an object to the map.
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type obj: GameObject
           @param obj: corresponding object class
           @type instance: fife.Instance
           @param instance: FIFE instance of object
           @return: None"""
        ref = self.game_state.getObjectById(obj.general.identifier,
                                            self.game_state.current_map_name) 
        if ref is None:
            # no, add it to the game state
            self.game_state.addObject(obj.general.identifier, 
                                      self.game_state.current_map_name, obj)
        else:
            # yes, use the current game state data
            obj.fifeagent.pos.X = ref.X
            obj.fifeagent.pos.Y = ref.Y
            obj.fifeagent.gfx = ref.gfx
             
        if obj.fifeagent.behaviour:
            obj.fifeagent.behaviour.parent = obj
            fifeagent.setup_behaviour(obj.fifeagent)
            obj.fifeagent.behaviour.speed = self.settings.get("parpg", "PCSpeed")
            #Start the behaviour            
            obj.fifeagent.behaviour.idle()
            # create the agent
            #obj.setup()
            #obj.behaviour.speed = self.settings.parpg.PCSpeed
            # create the PlayerCharacter agent
            #obj.start()
        #if obj.trueAttr("AnimatedContainer"):
            # create the agent
            #obj.setup()

    def objectActive(self, ident):
        """Given the objects ID, pass back the object if it is active,
           False if it doesn't exist or not displayed
           @type ident: string
           @param ident: ID of object
           @rtype: boolean
           @return: Status of result (True/False)"""
        for game_object in \
           self.game_state.getObjectsFromMap(self.game_state.current_map_name):
            if (game_object.general.identifier == ident):
                # we found a match
                return game_object
        # no match
        return False    

    def movePlayer(self, position):
        """Code called when the player should move to another location
           @type position: fife.ScreenPoint
           @param position: Screen position to move to
           @return: None"""
        player = self.game_state.getObjectById("PlayerCharacter")
        if(self.pc_run == 1):
            player.fifeagent.behaviour.run(position)
        else:
            player.fifeagent.behaviour.walk(position)
        
    def teleportAgent(self, agent, position):
        """Code called when an agent should teleport to another location
           @type position: fife.ScreenPoint
           @param position: Screen position to teleport to
           @return: None"""
        agent.teleport(position)
        self.agents[agent.ID]["Position"] = position

    def readObjectDB(self):
        """Reads the Object Information Database from a file. """
        database_file = vfs.VFS.open(self.object_db_file)
        database = yaml.load_all(database_file)
        for object_info in database:
            self.object_db.update(object_info)

    def updateObjectDB(self, world):
        """Updates the values in the object database with the worlds values"""
        
        all_agents = self.agents[self.ALL_AGENTS_KEY]
        for entity in world.entities:
            identifier = entity.general.identifier
            agent_data = {}
            map_id = self.game_state.getMapOfObject(identifier)
            if map_id:
                if all_agents.has_key(identifier):
                    agent_data = self.agents[self.ALL_AGENTS_KEY][identifier]
                else:
                    agent_data = self.agents[map_id][identifier]

            else:
                if not self.items.has_key(identifier):
                    self.items[identifier] = {}
                agent_data = self.items[identifier]
            entity_data = {}
            entity_data["general"] = {"identifier": identifier}
            for name, component in components.components.iteritems():
                if getattr(entity, name):
                    comp_data = {}
                    comp_vals = getattr(entity, name)
                    #Items that are in containers will be saved with them.
                    for field in component.saveable_fields:
                        try:
                            comp_data[field] = getattr(comp_vals, field)
                        except AttributeError:                            
                            #The entity doesn't have this specific value,
                            #ignore it
                            pass
                    if comp_data:
                        entity_data[name] = comp_data
                    if name == "fifeagent":
                        if entity.fifeagent.layer:
                            layer = entity.fifeagent.layer
                            inst = layer.getInstance(identifier)
                            loc = inst.getLocation().getExactLayerCoordinates()
                            agent_data["Position"] = (loc.x, loc.y, loc.z)
                            if all_agents.has_key(identifier):
                                agent_data["Map"] = map_id
                            agent_data["Rotation"]  = inst.getRotation()
                    elif name == "characterstats":
                        agent_data["Statistics"] = (
                            character_statistics.get_stat_values(
                                entity.characterstats
                            )["primary"]
                        )
                    elif name == "container" and hasattr(comp_vals, 
                                                         "children"):
                        inventory_data = {}
                        inventory_data["Slots"] = len(comp_vals.children)
                        items = []
                        for child in comp_vals.children:
                            if not child:
                                continue
                            items.append(
                                {"ID": child.entity.general.identifier,
                                 "Slot": child.slot}
                            )
                        inventory_data["Items"] = items
                        agent_data["Inventory"] = inventory_data
                    elif name == "equip":
                        equip_data = {}
                        for field in component.fields:
                            if(hasattr(comp_vals, field)):
                                equipable = getattr(comp_vals, field)
                                if equipable:
                                    equip_data[field] = {
                                        "ID": 
                                         equipable.entity.general.identifier
                                    }
                        agent_data["Equipment"] = equip_data
            agent_data["Entity"] = entity_data           
        
    def getAgentImportFiles(self):
        """Searches the agents directory for import files """
        filepaths = locateFiles("*.xml", self.objects_directory)
        for filepath in filepaths:
            try:
                xml_file = vfs.VFS.open(filepath)
                root = ElementTree.parse(xml_file).getroot()
                if root.tag == "object":
                    self.agent_import_files[root.attrib["id"]] = filepath
            except SyntaxError as error:
                logging.error("Error parsing file {0}: {1}".format(filepath,
                                                                   error))
    
    def getDialogues(self):
        """Searches the dialogue directory for dialogues """
        files = locateFiles("*.yaml", self.dialogue_directory)
        dialogue_parser = YamlDialogueParser()
        for dialogue_filepath in files:
            # Note Technomage 2010-11-13: the new DialogueEngine uses its own
            #     parser now, YamlDialogueParser.
#            dialogues = yaml.load_all(file(dialogue_file, "r"))
            dialogue_file = vfs.VFS.open(dialogue_filepath)
            try:
                dialogue = dialogue_parser.load(dialogue_file)
            except DialogueFormatError as error:
                logging.error('unable to load dialogue file {0}: {1}'
                              .format(dialogue_filepath, error))
            else:
                self.dialogues[dialogue.npc_name] = dialogue
class GameModel(object):
    """GameModel holds the logic for the game.
       Since some data (object position and so forth) is held in the
       fife, and would be pointless to replicate, we hold a instance of
       the fife view here. This also prevents us from just having a
       function heavy controller."""
    ALL_AGENTS_KEY = "All"
    MAX_ID_NUMBER = 1000
    
    def __init__(self, engine, settings):
        """Initialize the instance.
        @param engine: A fife.Engine object
        @type emgome: fife.Engine 
        @param setting: The applications settigns
        @type setting: fife_settings.Setting 
        @return: None"""
        self.map_change = False
        self.load_saver = False
        self.savegame = None
        self.game_state = GameState(quests_dir = settings.get("PARPG",
                                                             "QuestsDirectory"))
        #self.game_state.quest_engine = 
        #self.game_state.quest_engine.readQuests()
        self.pc_run = 1
        self.target_position = None
        self.target_map_name = None
        self.object_db = {}
        self.active_map = None
        self.map_files = {}
        self.agents = {}
        self.agents[self.ALL_AGENTS_KEY] = {}
        self.engine = engine
        self.fife_model = engine.getModel()
        self.game_state.maps_file = "maps/maps.yaml"
        self.all_agents_file = "maps/all_agents.yaml"
        self.object_db_file = "objects/object_database.yaml"
        self.agents_directory = "objects/"
        self.dialogues_directory = "dialogue"
        self.dialogues = {}
        self.agent_import_files = {}
        self.settings = settings
        self.obj_loader = XMLObjectLoader(
                                          self.engine.getImagePool(), 
                                          self.engine.getAnimationPool(), 
                                          self.engine.getModel(),
                                          self.engine.getVFS() 
                                          )

    def checkAttributes(self, attributes):
        """Checks for attributes that where not given in the map file
        and fills them with values from the object database
        @param attributes: attributes to check
        @type attributes: Dictionary
        @return: The modified attributes""" 
        if attributes.has_key("object_type"):
            class_name = attributes.pop("object_type")
        else:
            class_name = attributes["type"]
        if not attributes.has_key("type"):
            attributes["type"] = class_name
        if self.object_db.has_key(class_name):
            db_attributes = deepcopy(self.object_db[class_name])
            for key in db_attributes.keys():
                if attributes.has_key(key):
                    attributes[key] = attributes[key] or db_attributes[key]
                else:
                    attributes[key] = db_attributes[key]
        return attributes
    
    def isIDUsed(self, ID):
        if self.game_state.hasObject(ID):
            return True
        for namespace in self.agents:
            if ID in self.agents[namespace]:
                return True
        return False
    
    def createUniqueID(self, ID):
        if self.isIDUsed(ID):
            id_number = 1
            while self.isIDUsed(ID + "_" + str(id_number)):
                id_number += 1
                if id_number > self.MAX_ID_NUMBER:
                    raise ValueError(
                        "Number exceeds MAX_ID_NUMBER:" + str(self.MAX_ID_NUMBER))
            
            ID = ID + "_" + str(id_number)
        return ID

    def createContainerItems(self, container_objs):
        """Create the items of a container from a dictionary
        @param container_objs: Dictionary containing the items
        @type container_objs: dict"""
        items = []
        for container_obj in container_objs:
            items.append(self.createContainerObject(container_obj))
        
        return items

    def createContainerObject(self, attributes):
        """Create an object that can be stored in 
        an container and return it
        @param attributes: Dictionary of all object attributes
        @type attributes: Dictionary
        @return: The created object """
        # create the extra data
        extra = {}
        extra['controller'] = self
        attributes = self.checkAttributes(attributes)
        
        info = {}
        info.update(attributes)
        info.update(extra)
        ID = info.pop("id") if info.has_key("id") else info.pop("ID")
        if not info.has_key("item_type"):
            info["item_type"] = info["type"]
        ID = self.createUniqueID(ID)
        if info.has_key("attributes"):
            attributes = info["attributes"]
            if "Container" in attributes:
                info["actions"]["Open"] = ""
                if info.has_key("Items"):
                    inventory_objs = info["Items"]
                    info["items"] = self.createContainerItems(inventory_objs)
                
                new_item = CarryableContainer(ID = ID, **info) 
            else:
                new_item = CarryableItem(ID = ID, **info) 
        else:
            new_item = CarryableItem(ID = ID, **info) 
        self.game_state.addObject(None, new_item)
        return new_item
      
    def createInventoryObject(self, container, attributes):
        """Create an inventory object and place it into a container
           @type container: base.Container
           @param container: Container where the item is on
           @type attributes: Dictionary
           @param attributes: Dictionary of all object attributes
           @return: None"""
        index = attributes.pop("index") if attributes.has_key("index") else None
        slot = attributes.pop("slot") if attributes.has_key("slot") else None
        obj = self.createContainerObject(attributes)        
        #obj = createObject(attributes, extra)
        if slot:
            container.moveItemToSlot(obj, slot)
        else:
            container.placeItem(obj, index)
    
    def deleteObject(self, object_id):
        """Removes an object from the game
        @param object_id: ID of the object
        @type object_id: str """
        del self.agents["All"][object_id]
        self.game_state.deleteObject(object_id)
        
    def save(self, path, filename):
        """Writes the saver to a file.
           @type filename: string
           @param filename: the name of the file to write to
           @return: None"""
        fname = '/'.join([path, filename])
        try:
            save_file = open(fname, 'w')
        except(IOError):
            sys.stderr.write("Error: Can't create save game: " + fname + "\n")
            return
        save_state = {}
        save_state["Agents"] = {}
        for map_name in self.agents:
            if map_name == self.ALL_AGENTS_KEY:
                continue
            agents_dict = {}
            for agent in self.agents[map_name]:
                agent_obj = self.game_state.getObjectById(agent, map_name)
                agent_inst = self.game_state.maps[map_name].\
                                    agent_layer.getInstance(agent)
                agent_dict = self.agents[map_name][agent]
                agent_dict.update(agent_obj.getStateForSaving())
                agent_dict["Rotation"] = agent_inst.getRotation()
                agents_dict[agent] = agent_dict
            save_state["Agents"][map_name] = agents_dict
        agents_dict = {}
        for agent in self.agents["All"]:
            map_name = self.agents["All"][agent]["Map"]
            agent_dict = self.agents["All"][agent]
            agent_obj = None
            if agent == "PlayerCharacter":
                agent_obj = self.game_state.player_character
            else:
                agent_obj = self.game_state.getObjectById(agent, map_name)
            if agent_obj:
                agent_inst = self.game_state.maps[map_name].\
                                    agent_layer.getInstance(agent)
                agent_dict.update(agent_obj.getStateForSaving())
                agent_dict["Rotation"] = agent_inst.getRotation()
                agent_dict["MapName"] = map_name
            agents_dict[agent] = agent_dict
        save_state["Agents"]["All"] = agents_dict
        save_state["GameState"] = self.game_state.getStateForSaving()
        yaml.dump(save_state, save_file)
        
        save_file.close()       

    def load(self, path, filename):
        """Loads a saver from a file.
           @type filename: string
           @param filename: the name of the file (including path) to load from
           @return: None"""
        fname = '/'.join([path, filename])

        try:
            load_file = open(fname, 'r')
        except(IOError):
            sys.stderr.write("Error: Can't find save game file\n")
            return        
        self.deleteMaps()
        self.clearAgents()
        
        save_state = yaml.load(load_file)
        self.game_state.restoreFromState(save_state["GameState"])
        maps = save_state["Agents"]
        for map_name in maps:
            for agent_name in maps[map_name]:
                agent = {agent_name:maps[map_name][agent_name]}
                self.addAgent(map_name, agent)
                
        # Load the current map
        if self.game_state.current_map_name:
            self.loadMap(self.game_state.current_map_name)         
        load_file.close()
        

        # Recreate all the behaviours. These can't be saved because FIFE
        # objects cannot be pickled
        
        self.placeAgents()
        self.placePC()
      
        # In most maps we'll create the PlayerCharacter Instance internally. 
        # In these cases we need a target position
         
    def teleport(self, agent, position):
        """Called when a an agent is moved instantly to a new position. 
        The setting of position may wan to be created as its own method down the road.
        @type position: String Tuple
        @param position: X,Y coordinates passed from engine.changeMap
        @return: fife.Location"""
        print position
        coord = fife.DoublePoint3D(float(position[0]), float(position[1]), 0)
        location = fife.Location(self.active_map.agent_layer)
        location.setMapCoordinates(coord)
        agent.teleport(location)         
               
    def getObjectAtCoords(self, coords):
        """Get the object which is at the given coords
        @type coords: fife.Screenpoint
        @param coords: Coordinates where to check for an object
        @rtype: fife.Object
        @return: An object or None"""
        instances = self.active_map.cameras[
                                            self.active_map.my_cam_id].\
            getMatchingInstances(coords, self.active_map.agent_layer)
        # no object returns an empty tuple
        if(instances != ()):
            front_y = 0
            

            for obj in instances:
                # check to see if this in our list at all
                if(self.objectActive(obj.getId())):
                    # check if the object is on the foreground
                    obj_map_coords = \
                                      obj.getLocation().getMapCoordinates()
                    obj_screen_coords = self.active_map.\
                        cameras[self.active_map.my_cam_id]\
                        .toScreenCoordinates(obj_map_coords)

                    if obj_screen_coords.y > front_y:
                        #Object on the foreground
                        front_y = obj_screen_coords.y
                        return obj
                    else:
                        return None
        else:
            return None

    def getCoords(self, click):
        """Get the map location x, y coordinates from the screen coordinates
           @type click: fife.ScreenPoint
           @param click: Screen coordinates
           @rtype: fife.Location
           @return: The map coordinates"""
        coord = self.active_map.cameras[self.active_map.my_cam_id].\
                    toMapCoordinates(click, False)
        coord.z = 0
        location = fife.Location(self.active_map.agent_layer)
        location.setMapCoordinates(coord)
        return location

    def pause(self, paused):
        """ Pause/Unpause the game
        @return: nothing"""
        if self.active_map:
            self.active_map.pause(paused)
    
    def togglePause(self):
        """ Toggle paused state.
        @return: nothing"""
        self.active_map.togglePause()
        
    def isPaused(self):
        """Returns wheter the game is paused or not"""
        return self.active_map.isPaused()
    
    def readMapFiles(self):
        """Read all a available map-files and store them"""
        maps_data = file(self.game_state.maps_file)
        self.map_files = yaml.load(maps_data)["Maps"]
    
    def addAgent(self, namespace, agent):
        """Adds an agent to the agents dictionary
        @param namespace: the namespace where the agent is to be added to
        @type namespace: str
        @param agent: The agent to be added
        @type agent: dict """
        from fife.extensions.serializers.xml_loader_tools import loadImportFile
        if not self.agents.has_key(namespace):
            self.agents[namespace] = {}
            
        agent_values = agent.values()[0]
        unique_agent_id = self.createUniqueID(agent.keys()[0])
        del agent[agent.keys()[0]]
        agent[unique_agent_id] = agent_values
        self.agents[namespace].update(agent)
        object_model = ""
        if agent_values.has_key("ObjectModel"): 
            object_model =  agent_values["ObjectModel"]
        elif agent_values["ObjectType"] == "MapItem":
            object_data = self.object_db[agent_values["ItemType"]]
            object_model = object_data["gfx"] if object_data.has_key("gfx") \
                        else "generic_item"
        else:
            object_model = self.object_db[agent_values["ObjectType"]]["gfx"]
        import_file = self.agent_import_files[object_model]
        loadImportFile(self.obj_loader, import_file, self.engine)
        
    def readAgentsOfMap(self, map_name):
        """Read the agents of the map
        @param map_name: Name of the map
        @type map_name: str """
        #Get the agents of the map        
        map_agents_file = self.map_files[map_name].\
                            replace(".xml", "_agents.yaml")   
        agents_data = file(map_agents_file)
        agents = yaml.load_all(agents_data)
        for agent in agents:
            if not agent == None:
                self.addAgent(map_name, agent)  
    
    def readAllAgents(self):
        """Read the agents of the all_agents_file and store them"""
        agents_data = file(self.all_agents_file)
        agents = yaml.load_all(agents_data)
        for agent in agents:
            if not agent == None:
                self.addAgent(self.ALL_AGENTS_KEY, agent)  
                
    def getAgentsOfMap(self, map_name):
        """Returns the agents that are on the given map
        @param map_name: Name of the map
        @type map_name: str
        @return: A dictionary with the agents of the map"""
        if not self.agents.has_key(map_name):
            return {}
        ret_dict = self.agents[map_name].copy()
        for agent_name, agent_value in self.agents[self.ALL_AGENTS_KEY]\
                                                .iteritems():
            if agent_value["Map"] == map_name:
                ret_dict[agent_name] = agent_value
        return ret_dict
                
    def getAgentsOfActiveMap(self):
        """Returns the agents that are on active map
        @return: A dictionary with the agents of the map """
        return self.getAgentsOfMap(self.active_map.map.getId())

    def clearAgents(self):
        """Resets the agents dictionary"""
        self.agents = {}
        self.agents[self.ALL_AGENTS_KEY] = {}
    
    def loadMap(self, map_name):
        """Load a new map.
           @type map_name: string
           @param map_name: Name of the map to load
           @return: None"""
        if not map_name in self.game_state.maps:  
            map_file = self.map_files[map_name]
            new_map = GameMap(self.engine, self)
            self.game_state.maps[map_name] = new_map
            new_map.load(map_file)    

    def createAgent(self, agent, inst_id):
        object_type = agent["ObjectType"]
        object_id = agent["ObjectModel"] \
                                if agent.has_key("ObjectModel") \
                                else None
        if object_id == None:
            if object_type == "MapItem":
                object_data = self.object_db[agent["ItemType"]]
                object_id = object_data["gfx"] if object_data.has_key("gfx") \
                            else "generic_item"
            else:
                object_id = self.object_db[object_type]["gfx"]
        map_obj = self.fife_model.getObject(str(object_id), "PARPG")
        if not map_obj:
            print ''.join(['Object with inst_id=', str(object_id), 
                           ' ns=PARPG', \
                           ' could not be found. Omitting...'])

        x_pos = agent["Position"][0]
        y_pos = agent["Position"][1]
        z_pos = agent["Position"][2] if len(agent["Position"]) == 3 \
                                        else -0.1 if object_type == "MapItem" \
                                        else 0.0  
        stack_pos = agent["Stackposition"] if \
                        agent.has_key("StackPosition") \
                        else None
        inst = self.active_map.agent_layer.\
                        createInstance(map_obj,
                                       fife.ExactModelCoordinate(x_pos, 
                                                                 y_pos, 
                                                                 z_pos),
                                       inst_id)
        inst.setId(inst_id)

        rotation = agent["Rotation"]
        inst.setRotation(rotation)

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

        if (map_obj.getAction('default')):
            target = fife.Location(self.active_map.agent_layer)
            inst.act('default', target, True)
            
        inst_dict = {}
        inst_dict["id"] = inst_id
        inst_dict["type"] = object_type
        inst_dict["xpos"] = x_pos
        inst_dict["ypos"] = y_pos
        inst_dict["gfx"] = object_id
        inst_dict["is_open"] = parseBool(agent["Open"]) \
                                if agent.has_key("Open") \
                                else False
        inst_dict["locked"] = parseBool(agent["Locked"]) \
                                if agent.has_key("Locked") \
                                else False
        inst_dict["name"] = agent["ViewName"]
        inst_dict["real_name"] = agent["RealName"] \
                                    if agent.has_key("RealName") \
                                    else agent["ViewName"]
        inst_dict["text"] = agent["Text"] \
                                    if agent.has_key("Text") \
                                    else None
        if self.dialogues.has_key(inst_id):
            inst_dict["dialogue"] = self.dialogues[inst_id]
        inst_dict["target_map_name"] = agent["TargetMap"] \
                                        if agent.\
                                            has_key("TargetMap") \
                                        else None
        inst_dict["target_x"] = agent["TargetPosition"][0] \
                                    if agent.\
                                        has_key("TargetPosition") \
                                    else None
        inst_dict["target_y"] = agent["TargetPosition"][1] \
                                    if agent.\
                                        has_key("TargetPosition") \
                                    else None
        if agent.has_key("Inventory"):
            inventory = Inventory()
            inventory_objs = agent["Inventory"]
            for inventory_obj in inventory_objs:
                self.createInventoryObject(inventory,
                                           inventory_obj 
                                           )
            inst_dict["inventory"] = inventory

        if agent.has_key("Items"):
            container_objs = agent["Items"]
            items = self.createContainerItems(container_objs)
            inst_dict["items"] = items
            
        if agent.has_key("ItemType"):
            if not agent.has_key("item"):
                item_data = {}
                item_data["type"] = agent["ItemType"]
                item_data["ID"] = inst_id 
                item_data = self.createContainerObject(item_data)
            else:
                item_data = agent["item"]
            inst_dict["item"] = item_data
            inst_dict["item_type"] = agent["ItemType"]

        self.createMapObject(self.active_map.agent_layer, inst_dict)
    
    def placeAgents(self):
        """Places the current maps agents """
        if not self.active_map:
            return
        agents = self.getAgentsOfMap(self.game_state.current_map_name)
        for agent in agents:
            if agent == "PlayerCharacter":
                continue
            if self.active_map.agent_layer.getInstances(agent):
                continue
            self.createAgent(agents[agent], agent)

    def placePC(self):
        """Places the PlayerCharacter on the map"""
        agent = self.agents[self.ALL_AGENTS_KEY]["PlayerCharacter"]
        inst_id = "PlayerCharacter"
        self.createAgent(agent, inst_id)
        
        # create the PlayerCharacter agent
        self.active_map.addPC()
        self.game_state.player_character.start()
        if agent.has_key("PeopleKnown"):
            self.game_state.player_character.people_i_know = agent["PeopleKnown"]
                      
    def changeMap(self, map_name, target_position = None):
        """Registers for a map change on the next pump().
           @type map_name: String
           @param map_name: Id of the map to teleport to
           @type map_file: String
           @param map_file: Filename of the map to teleport to
           @type target_position: Tuple
           @param target_position: Position of PlayerCharacter on target map.
           @return None"""
        # set the parameters for the map change if moving to a new map
        if map_name != self.game_state.current_map_name:
            self.target_map_name = map_name
            self.target_position = target_position
            # issue the map change
            self.map_change = True

    def deleteMaps(self):
        """Clear all currently loaded maps from FIFE as well as clear our
            local map cache
            @return: nothing"""
        self.engine.getModel().deleteMaps()
        self.engine.getModel().deleteObjects()
        self.game_state.clearObjects()
        self.game_state.maps = {}
        
    def setActiveMap(self, map_name):
        """Sets the active map that is to be rendered.
           @type map_name: String
           @param map_name: The name of the map to load
           @return: None"""
        # Turn off the camera on the old map before we turn on the camera
        # on the new map.
        self.active_map.cameras[self.active_map.my_cam_id].setEnabled(False)
        # Make the new map active.
        self.active_map = self.game_state.maps[map_name]
        self.active_map.makeActive()
        self.game_state.current_map_name = map_name

    def createMapObject (self, layer, attributes):
        """Create an object and add it to the current map.
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type attributes: Dictionary
           @param attributes: Dictionary of all object attributes
           @type instance: fife.Instance
           @param instance: FIFE instance corresponding to the object
           @return: None"""
        # create the extra data
        extra = {}
        if layer is not None:
            extra['agent_layer'] = layer
        attributes = self.checkAttributes(attributes)
        
        obj = createObject(attributes, extra)
        
        if obj.trueAttr("PC"):
            self.addPC(layer, obj)
        else:
            self.addObject(layer, obj) 

    def addPC(self, layer, player_char):
        """Add the PlayerCharacter to the map
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type player_char: PlayerCharacter
           @param player_char: PlayerCharacter object
           @type instance: fife.Instance
           @param instance: FIFE instance of PlayerCharacter
           @return: None"""
        # For now we copy the PlayerCharacter, 
        # in the future we will need to copy
        # PlayerCharacter specifics between the different PlayerCharacter's
        self.game_state.player_character = player_char
        self.game_state.player_character.setup()        

    def addObject(self, layer, obj):
        """Adds an object to the map.
           @type layer: fife.Layer
           @param layer: FIFE layer object exists in
           @type obj: GameObject
           @param obj: corresponding object class
           @type instance: fife.Instance
           @param instance: FIFE instance of object
           @return: None"""
        ref = self.game_state.getObjectById(obj.ID, \
                                            self.game_state.current_map_name) 
        if ref is None:
            # no, add it to the game state
            self.game_state.addObject(self.game_state.current_map_name, obj)
        else:
            # yes, use the current game state data
            obj.X = ref.X
            obj.Y = ref.Y
            obj.gfx = ref.gfx  
             
        if obj.trueAttr("NPC"):
            # create the agent
            obj.setup()
            # create the PlayerCharacter agent
            obj.start()
        if obj.trueAttr("AnimatedContainer"):
            # create the agent
            obj.setup()

    def objectActive(self, ident):
        """Given the objects ID, pass back the object if it is active,
           False if it doesn't exist or not displayed
           @type ident: string
           @param ident: ID of object
           @rtype: boolean
           @return: Status of result (True/False)"""
        for game_object in \
           self.game_state.getObjectsFromMap(self.game_state.current_map_name):
            if (game_object.ID == ident):
                # we found a match
                return game_object
        # no match
        return False    

    def movePlayer(self, position):
        """Code called when the player should move to another location
           @type position: fife.ScreenPoint
           @param position: Screen position to move to
           @return: None"""
        if(self.pc_run == 1):
            self.game_state.player_character.run(position)
        else:
            self.game_state.player_character.walk(position)
        
    def teleportAgent(self, agent, position):
        """Code called when an agent should teleport to another location
           @type position: fife.ScreenPoint
           @param position: Screen position to teleport to
           @return: None"""
        agent.teleport(position)
        self.agents[agent.ID]["Position"] = position

    def readObjectDB(self):
        """Reads the Object Information Database from a file. """
        database_file = file(self.object_db_file, "r")
        database = yaml.load_all(database_file)
        for object_info in database:
            self.object_db.update(object_info)

    def getAgentImportFiles(self):
        """Searches the agents directory for import files """
        files = locateFiles("*.xml", self.agents_directory)
        for xml_file in files:
            xml_file = os.path.relpath(xml_file).replace("\\", "/")
            try:
                root = ElementTree.parse(xml_file).getroot()
                if root.tag == "object":
                    self.agent_import_files[root.attrib["id"]] = xml_file
            except SyntaxError as error:
                assert(isinstance(error, SyntaxError))
                print "Error parsing file " + xml_file + ": " + error.msg
                #TODO: We may want to make this an fatal error later.
    
    def getDialogues(self):
        """Searches the dialogue directory for dialogues """
        files = locateFiles("*.yaml", self.dialogues_directory)
        dialogue_parser = YamlDialogueParser()
        for dialogue_filepath in files:
            dialogue_filepath = os.path.relpath(dialogue_filepath) \
                                .replace("\\", "/")
            # Note Technomage 2010-11-13: the new DialogueEngine uses its own
            #     parser now, YamlDialogueParser.
#            dialogues = yaml.load_all(file(dialogue_file, "r"))
            with file(dialogue_filepath, 'r') as dialogue_file:
                try:
                    dialogue = dialogue_parser.load(dialogue_file)
                except (DialogueFormatError,) as error:
                    logging.error('unable to load dialogue file {0}: {1}'
                                  .format(dialogue_filepath, error))
                else:
                    self.dialogues[dialogue.npc_name] = dialogue