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()
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()
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