Mercurial > parpg-source
view gamescenecontroller.py @ 0:7a89ea5404b1
Initial commit of parpg-core.
author | M. George Hansen <technopolitica@gmail.com> |
---|---|
date | Sat, 14 May 2011 01:12:35 -0700 |
parents | |
children | 06145a6ee387 |
line wrap: on
line source
# This file is part of PARPG. # PARPG is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # PARPG is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with PARPG. If not, see <http://www.gnu.org/licenses/>. """This file contains the GameSceneController that handles input when the game is exploring a scene""" from datetime import datetime import random import glob import os from fife import fife from fife import extensions from controllerbase import ControllerBase from parpg.gui.hud import Hud from parpg.gui import drag_drop_data as data_drag from objects.action import ChangeMapAction, ExamineAction, OpenBoxAction, \ UnlockBoxAction, LockBoxAction, TalkAction, \ PickUpAction, DropItemAction #For debugging/code analysis if False: from gamesceneview import GameSceneView from gamemodel import GameModel from parpg import PARPGApplication import logging logger = logging.getLogger('gamescenecontroller') class GameSceneController(ControllerBase): ''' This controller handles inputs when the game is in "scene" state. "Scene" state is when the player can move around and interact with objects. Like, talking to a npc or examining the contents of a box. ''' def __init__(self, engine, view, model, application): ''' Constructor @param engine: Instance of the active fife engine @type engine: fife.Engine @param view: Instance of a GameSceneView @param type: parpg.GameSceneView @param model: The model that has the current gamestate @type model: parpg.GameModel @param application: The application that created this controller @type application: parpg.PARPGApplication @param settings: The current settings of the application @type settings: fife.extensions.fife_settings.Setting ''' ControllerBase.__init__(self, engine, view, model, application) #this can be helpful for IDEs code analysis if False: assert(isinstance(self.engine, fife.Engine)) assert(isinstance(self.view, GameSceneView)) assert(isinstance(self.view, GameModel)) assert(isinstance(self.application, PARPGApplication)) assert(isinstance(self.event_manager, fife.EventManager)) # Last saved mouse coords self.action_number = 1 self.has_mouse_focus = True self.last_mousecoords = None self.mouse_callback = None self.original_cursor_id = self.engine.getCursor().getId() self.scroll_direction = [0, 0] self.scroll_timer = extensions.fife_timer.Timer(100, lambda: self.view.moveCamera \ (self.scroll_direction)) #this is temporary until we can set the native cursor self.resetMouseCursor() self.paused = False if model.settings.fife.EnableSound: if not self.view.sounds.music_init: music_file = random.choice(glob.glob(os.path.join( "music", "*.ogg"))) self.view.sounds.playMusic(music_file) self.initHud() def initHud(self): """Initialize the hud member @return: None""" hud_callbacks = { 'saveGame': self.saveGame, 'loadGame': self.loadGame, 'quitGame': self.quitGame, } self.view.hud = Hud(self, self.model.settings, hud_callbacks) def keyPressed(self, evt): """Whenever a key is pressed, fife calls this routine. @type evt: fife.event @param evt: The event that fife caught @return: None""" key = evt.getKey() key_val = key.getValue() if(key_val == key.Q): # we need to quit the game self.view.hud.quitGame() if(key_val == key.T): self.model.active_map.toggleRenderer('GridRenderer') if(key_val == key.F1): # display the help screen and pause the game self.view.hud.displayHelp() if(key_val == key.F5): self.model.active_map.toggleRenderer('CoordinateRenderer') if(key_val == key.F7): # F7 saves a screenshot to screenshots directory settings = self.model.settings screenshot_directory = os.path.join(settings.user_path, settings.parpg.ScreenshotsPath) # try to create the screenshots directory try: os.mkdir(screenshot_directory) #TODO: distinguish between already existing permissions error except OSError: logger.warning("screenshot directory wasn't created.") screenshot_file = os.path.join(screenshot_directory, 'screen-{0}.png'.format( datetime.now().strftime( '%Y-%m-%d-%H-%M-%S'))) self.engine.getRenderBackend().captureScreen(screenshot_file) logger.info("PARPG: Saved: {0}".format(screenshot_file)) if(key_val == key.F10): # F10 shows/hides the console self.engine.getGuiManager().getConsole().toggleShowHide() if(key_val == key.C): # C opens and closes the character screen. self.view.hud.toggleCharacterScreen() if(key_val == key.I): # I opens and closes the inventory self.view.hud.toggleInventory() if(key_val == key.A): # A adds a test action to the action box # The test actions will follow this format: Action 1, # Action 2, etc. self.view.hud.addAction("Action " + str(self.action_number)) self.action_number += 1 if(key_val == key.ESCAPE): # Escape brings up the main menu self.view.hud.displayMenu() # Hide the quit menu self.view.hud.quit_window.hide() if(key_val == key.M): self.view.sounds.toggleMusic() if(key_val == key.PAUSE): # Pause pause/unpause the game self.model.togglePause() self.pause(False) if(key_val == key.SPACE): self.model.active_map.centerCameraOnPlayer() def mouseReleased(self, evt): """If a mouse button is released, fife calls this routine. We want to wait until the button is released, because otherwise pychan captures the release if a menu is opened. @type evt: fife.event @param evt: The event that fife caught @return: None""" self.view.hud.hideContextMenu() scr_point = fife.ScreenPoint(evt.getX(), evt.getY()) if(evt.getButton() == fife.MouseEvent.LEFT): if(data_drag.dragging): coord = self.model.getCoords(scr_point)\ .getExactLayerCoordinates() commands = ({"Command": "ResetMouseCursor"}, {"Command": "StopDragging"}) self.model.game_state.player_character.approach([coord.x, coord.y], DropItemAction(self, data_drag.dragged_item, commands)) else: self.model.movePlayer(self.model.getCoords(scr_point)) elif(evt.getButton() == fife.MouseEvent.RIGHT): # is there an object here? tmp_active_map = self.model.active_map instances = tmp_active_map.cameras[tmp_active_map.my_cam_id].\ getMatchingInstances(scr_point, tmp_active_map.agent_layer) info = None for inst in instances: # check to see if this is an active item if(self.model.objectActive(inst.getId())): # yes, get the model info = self.getItemActions(inst.getId()) break # take the menu items returned by the engine or show a # default menu if no items data = info or \ [["Walk", "Walk here", self.view.onWalk, self.model.getCoords(scr_point)]] # show the menu self.view.hud.showContextMenu(data, (scr_point.x, scr_point.y)) def updateMouse(self): """Updates the mouse values""" if self.paused: return cursor = self.engine.getCursor() #this can be helpful for IDEs code analysis if False: assert(isinstance(cursor, fife.Cursor)) self.last_mousecoords = fife.ScreenPoint(cursor.getX(), cursor.getY()) self.view.highlightFrontObject(self.last_mousecoords) #set the trigger area in pixles pixle_edge = 20 mouse_x = self.last_mousecoords.x screen_width = self.model.engine.getSettings().getScreenWidth() mouse_y = self.last_mousecoords.y screen_height = self.model.engine.getSettings().getScreenHeight() image = None settings = self.model.settings #edge logic self.scroll_direction = [0, 0] if self.has_mouse_focus: direction = self.scroll_direction #up if mouse_y <= pixle_edge: direction[0] += 1 direction[1] -= 1 image = os.path.join(settings.system_path, settings.parpg.GuiPath, settings.parpg.CursorPath, settings.parpg.CursorUp) #right if mouse_x >= screen_width - pixle_edge: direction[0] += 1 direction[1] += 1 image = os.path.join(settings.system_path, settings.parpg.GuiPath, settings.parpg.CursorPath, settings.parpg.CursorRight) #down if mouse_y >= screen_height - pixle_edge: direction[0] -= 1 direction[1] += 1 image = os.path.join(settings.system_path, settings.parpg.GuiPath, settings.parpg.CursorPath, settings.parpg.CursorDown) #left if mouse_x <= pixle_edge: direction[0] -= 1 direction[1] -= 1 image = os.path.join(settings.system_path, settings.parpg.GuiPath, settings.parpg.CursorPath, settings.parpg.CursorLeft) if image is not None and not data_drag.dragging: self.setMouseCursor(image, image) def handleCommands(self): """Check if a command is to be executed """ if self.model.map_change: self.pause(True) if self.model.active_map: player_char = self.model.game_state.player_character self.model.game_state.player_character = None pc_agent = self.model.agents[self.model.ALL_AGENTS_KEY]\ ["PlayerCharacter"] pc_agent.update(player_char.getStateForSaving()) pc_agent["Map"] = self.model.target_map_name pc_agent["Position"] = self.model.target_position pc_agent["Inventory"] = \ player_char.inventory.serializeInventory() player_agent = self.model.active_map.\ agent_layer.getInstance("PlayerCharacter") self.model.active_map.agent_layer.deleteInstance(player_agent) self.model.loadMap(self.model.target_map_name) self.model.setActiveMap(self.model.target_map_name) self.model.readAgentsOfMap(self.model.target_map_name) self.model.placeAgents() self.model.placePC() self.model.map_change = False # The PlayerCharacter 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.inventory = None self.view.hud.initializeInventory() self.pause(False) def nullFunc(self, userdata): """Sample callback for the context menus.""" logger.info(userdata) def initTalk(self, npc_info): """ Starts the PlayerCharacter talking to an NPC. """ # TODO: work more on this when we get NPCData and HeroData straightened # out npc = self.model.game_state.getObjectById(npc_info.ID, self.model.game_state.\ current_map_name) self.model.game_state.player_character.approach([npc.getLocation().\ getLayerCoordinates().x, npc.getLocation().\ getLayerCoordinates().y], TalkAction(self, npc)) 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.model.game_state.\ getObjectById(obj_id, self.model.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.model.game_state.\ player_character.approach, [obj.X, obj.Y], ExamineAction(self, obj_id, obj.name, obj.text)]) # is it a Door? if obj.trueAttr("door"): actions.append(["Change Map", "Change Map", self.model.game_state.player_character.approach, [obj.X, obj.Y], ChangeMapAction(self, obj.target_map_name, obj.target_pos)]) # is it a container? if obj.trueAttr("container"): actions.append(["Open", "Open", self.model.game_state.\ player_character.approach, [obj.X, obj.Y], OpenBoxAction(self, obj)]) actions.append(["Unlock", "Unlock", self.model.game_state.\ player_character.approach, [obj.X, obj.Y], UnlockBoxAction(self, obj)]) actions.append(["Lock", "Lock", self.model.game_state.\ player_character.approach, [obj.X, obj.Y], LockBoxAction(self, obj)]) # can you pick it up? if obj.trueAttr("carryable"): actions.append(["Pick Up", "Pick Up", self.model.game_state.\ player_character.approach, [obj.X, obj.Y], PickUpAction(self, obj)]) return actions def saveGame(self, *args, **kwargs): """Saves the game state, delegates call to engine.Engine @return: None""" self.model.pause(False) self.pause(False) self.view.hud.enabled = True self.model.save(*args, **kwargs) def loadGame(self, *args, **kwargs): """Loads the game state, delegates call to engine.Engine @return: None""" # Remove all currently loaded maps so we can start fresh self.model.pause(False) self.pause(False) self.view.hud.enabled = True self.model.deleteMaps() self.view.hud.inventory = None self.model.load(*args, **kwargs) self.view.hud.initializeInventory() def quitGame(self): """Quits the game @return: None""" self.application.listener.quitGame() def pause(self, paused): """Pauses the controller""" super(GameSceneController, self).pause(paused) self.paused = paused if paused: self.scroll_timer.stop() self.resetMouseCursor() def onCommand(self, command): if(command.getCommandType() == fife.CMD_MOUSE_FOCUS_GAINED): self.has_mouse_focus = True elif(command.getCommandType() == fife.CMD_MOUSE_FOCUS_LOST): self.has_mouse_focus = False def pump(self): """Routine called during each frame. Our main loop is in ./run.py""" # uncomment to instrument # t0 = time.time() if self.paused: return self.updateMouse() if self.model.active_map: self.view.highlightFrontObject(self.last_mousecoords) self.view.refreshTopLayerTransparencies() if self.scroll_direction != [0, 0]: self.scroll_timer.start() else: self.scroll_timer.stop() if not data_drag.dragging: self.resetMouseCursor() self.handleCommands() # print "%05f" % (time.time()-t0,)