Mercurial > parpg-source
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gamescenecontroller.py Sat May 14 01:12:35 2011 -0700 @@ -0,0 +1,461 @@ +# 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,)