Mercurial > parpg-core
diff src/parpg/gui/hud.py @ 0:1fd2201f5c36
Initial commit of parpg-core.
author | M. George Hansen <technopolitica@gmail.com> |
---|---|
date | Sat, 14 May 2011 01:12:35 -0700 |
parents | |
children | d60f1dab8469 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parpg/gui/hud.py Sat May 14 01:12:35 2011 -0700 @@ -0,0 +1,505 @@ +# 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/>. + +import os +import logging + +from fife.extensions import pychan +from fife.extensions.pychan.tools import callbackWithArguments as cbwa + +from parpg.gui.filebrowser import FileBrowser +from parpg.gui.menus import ContextMenu, SettingsMenu +from parpg.gui import inventorygui +from parpg.gui.popups import ExaminePopup +from parpg.gui.containergui import ContainerGUI +from parpg.gui.dialoguegui import DialogueGUI +from parpg.gui import drag_drop_data as data_drag +from actionsbox import ActionsBox + +logger = logging.getLogger('hud') +class Hud(object): + """Main Hud class""" + def __init__(self, controller, settings, callbacks): + """Initialise the instance. + @type controller: Class derived from ControllerBase + @param controller: The current controller + @type settings: settings.Setting + @param settings: The settings + @type inv_model: dict + @type callbacks: dict + @param callbacks: a dict of callbacks + saveGame: called when the user clicks on Save + loadGame: called when the user clicks on Load + quitGame: called when the user clicks on Quit + @return: None""" + + # TODO: perhaps this should not be hard-coded here + pychan.registerWidget(ActionsBox) + self.hud = pychan.loadXML("gui/hud.xml") + self.controller = controller + self.engine = controller.engine + self.model = controller.model + self.settings = settings + self.inventory = None + self.character_screen = None + + self.save_game_callback = callbacks['saveGame'] + self.load_game_callback = callbacks['loadGame'] + self.quit_callback = callbacks['quitGame'] + + self.box_container = None + self.examine_box = None + self.context_menu = None + self.help_dialog = None + self.events_to_map = None + self.main_menu = None + self.menu_events = None + self.quit_window = None + self.bottom_panel = self.hud.findChild(name="mainHudWindow") + + self.actions_box = self.hud.findChild(name="actionsBox") + self.menu_displayed = False + self.inventory_storage = None + self.initializeHud() + self.initializeMainMenu() + self.initializeContextMenu() + self.initializeHelpMenu() + self.initializeEvents() + self.initializeQuitDialog() + self.initializeSettingsMenu() + + def _getEnabled(self): + """"Returns whether the gui widget is enabled or not""" + return self.hud.real_widget.isEnabled() + + def _setEnabled(self, enabled): + """"Sets whether the gui widget is enabled or not""" + self.hud.real_widget.setEnabled(enabled) + childs = self.hud.getNamedChildren() + for child_list in childs.itervalues(): + for child in child_list: + child.real_widget.setEnabled(enabled) + + enabled = property(_getEnabled, _setEnabled) + + def initializeHud(self): + """Initialize and show the main HUD + @return: None""" + self.events_to_map = {"menuButton":self.displayMenu, } + self.hud.mapEvents(self.events_to_map) + # set HUD size according to screen size + screen_width = self.engine.getSettings().getScreenWidth() + self.hud.findChild(name="mainHudWindow").size = (screen_width, 65) + self.hud.findChild(name="inventoryButton").position = \ + (screen_width-59, 7) + # add ready slots + ready1 = self.hud.findChild(name='hudReady1') + ready2 = self.hud.findChild(name='hudReady2') + ready3 = self.hud.findChild(name='hudReady3') + ready4 = self.hud.findChild(name='hudReady4') + + if (screen_width <=800) : + gap = 0 + else : + gap = 40 + ready1.position = (160+gap, 7) + ready2.position = (220+gap, 7) + ready3.position = (screen_width-180-gap, 7) + ready4.position = (screen_width-120-gap, 7) + self.actions_box.position = (280+gap, 5) + actions_width = screen_width - 470 - 2*gap + + self.actions_box.ContentBox.min_width = actions_width + self.actions_box.ContentBox.max_width = actions_width + + # and finally add an actions box + self.actions_box.min_size = (actions_width, 55) + self.actions_box.max_size = (actions_width, 55) + # now it should be OK to display it all + self.showHUD() + + def addAction(self, action): + """Add an action to the actions box. + @type action: (unicode) string + @param action: The text that you want to display in the actions box + @return: None""" + self.actions_box.addAction(action) + + def showHUD(self): + """Show the HUD. + @return: None""" + self.hud.show() + self.enabled = True + + def hideHUD(self): + """Hide the HUD. + @return: None""" + self.hud.hide() + self.enabled = False + + def initializeInventory(self): + """Initialize the inventory""" + if not self.inventory: + self.inventory = inventorygui.InventoryGUI(self.controller, + None, + None) +# inv_callbacks = { +# 'refreshReadyImages': self.refreshReadyImages, +# 'toggleInventoryButton': self.toggleInventoryButton, +# } +# self.inventory_storage = \ +# self.model.game_state.player_character.inventory +# if self.inventory == None: +# self.inventory = inventorygui.InventoryGUI(self.controller, +# self.inventory_storage, +# inv_callbacks) +# else: +# self.inventory.inventory_storage = self.inventory_storage +# self.refreshReadyImages() + + def initializeCharacterScreen(self): + """Initialize the character screen.""" + # TODO Technomage 2010-12-24: + if not self.character_screen: + self.character_screen = pychan.loadXML('gui/character_screen.xml') + + + def initializeContextMenu(self): + """Initialize the Context Menu + @return: None""" + self.context_menu = ContextMenu (self.engine, [], (0, 0)) + + def showContextMenu(self, data, pos): + """Display the Context Menu with model at pos + @type model: list + @param model: model to pass to context menu + @type pos: tuple + @param pos: tuple of x and y coordinates + @return: None""" + self.context_menu = ContextMenu(self.engine, data, pos) + self.context_menu.show() + + def hideContextMenu(self): + """Hides the context menu + @return: None""" + self.context_menu.hide() + + def initializeMainMenu(self): + """Initalize the main menu. + @return: None""" + self.main_menu = pychan.loadXML("gui/hud_pause_menu.xml") + #TODO: find more suitalbe place for onOptilonsPress implementation + self.menu_events = {"resumeButton": self.hideMenu, + "settingsButton": self.displaySettings, + "helpButton": self.displayHelp} + self.main_menu.mapEvents(self.menu_events) + + def displayMenu(self): + """Displays the main in-game menu. + @return: None""" + self.stopActions() + if (self.menu_displayed == False): + self.main_menu.show() + self.menu_displayed = True + self.model.pause(True) + self.controller.pause(True) + self.enabled = False + elif (self.menu_displayed == True): + self.hideMenu() + + def hideMenu(self): + """Hides the main in-game menu. + @return: None""" + self.main_menu.hide() + self.menu_displayed = False + self.model.pause(False) + self.controller.pause(False) + self.enabled = True + + def initializeSettingsMenu(self): + self.settings_menu = SettingsMenu(self.engine, self.settings) + + def displaySettings(self): + self.settings_menu.show() + + def initializeHelpMenu(self): + """Initialize the help menu + @return: None""" + self.help_dialog = pychan.loadXML("gui/help.xml") + help_events = {"closeButton":self.help_dialog.hide} + self.help_dialog.mapEvents(help_events) + main_help_text = u"Welcome to Post-Apocalyptic RPG or PARPG![br][br]"\ + "This game is still in development, so please expect for there to be "\ + "bugs and[br]feel free to tell us about them at "\ + "http://www.forums.parpg.net.[br]This game uses a "\ + "\"Point 'N' Click\" interface, which means that to move around,[br]"\ + "just click where you would like to go and your character will move "\ + "there.[br]PARPG also utilizes a context menu. To access this, just "\ + "right click anywhere[br]on the screen and a menu will come up. This "\ + "menu will change depending on[br]what you have clicked on, hence "\ + "it's name \"context menu\".[br][br]" + + k_text = u" Keybindings" + k_text += "[br] A : Add a test action to the actions display" + k_text += "[br] I : Toggle the inventory screen" + k_text += "[br] F7 : Take a screenshot" + k_text += "[br] (Saves to screenshots directory)" + k_text += "[br] F10 : Toggle console" + k_text += "[br] PAUSE : (Un)Pause the game" + k_text += "[br] Q : Quit the game" + self.help_dialog.distributeInitialData({ + "MainHelpText":main_help_text, + "KeybindText":k_text + }) + + def displayHelp(self): + """Display the help screen. + @return: None""" + self.help_dialog.show() + + def saveGame(self): + """ Called when the user wants to save the game. + @return: None""" + self.stopActions() + xml_path = os.path.join(self.settings.system_path, + self.settings.parpg.GuiPath, + 'savebrowser.xml') + save_browser = FileBrowser(self.engine, + self.settings, + self.save_game_callback, + xml_path, + self.loadsave_close, + save_file=True, + extensions=('.dat')) + save_browser.showBrowser() + self.controller.pause(True) + self.model.pause(True) + self.enabled = False + + def stopActions(self): + """This method stops/resets actions that are currently performed + like dragging an item. + This is done to be able to savely perform other actions that might + interfere with current running ones.""" + #Reset dragging - move item back to its old container + if data_drag.dragging: + data_drag.source_container.placeItem(data_drag.dragged_item) + data_drag.dragging = False + data_drag.dragged_item = None + if self.inventory: + self.inventory.closeInventory() + + def newGame(self): + """Called when user request to start a new game. + @return: None""" + self.stopActions() + logger.info('new game') + + def loadsave_close(self): + """Called when the load/save filebrowser was closed without a file selected""" + if not self.menu_displayed: + self.enabled = True + self.model.pause(False) + self.controller.pause(False) + + def loadGame(self): + """ Called when the user wants to load a game. + @return: None""" + self.stopActions() + xml_path = os.path.join(self.settings.system_path, + self.settings.parpg.GuiPath, + 'loadbrowser.xml') + load_browser = FileBrowser(self.engine, + self.settings, + self.load_game_callback, + xml_path, + close_callback = self.loadsave_close, + save_file=False, + extensions=('.dat')) + load_browser.showBrowser() + self.model.pause(True) + self.controller.pause(True) + self.enabled = False + + def initializeQuitDialog(self): + """Creates the quit confirmation dialog + @return: None""" + self.quit_window = pychan.widgets.Window(title=unicode("Quit?"), \ + min_size=(200,0)) + + hbox = pychan.widgets.HBox() + are_you_sure = "Are you sure you want to quit?" + label = pychan.widgets.Label(text=unicode(are_you_sure)) + yes_button = pychan.widgets.Button(name="yes_button", + text=unicode("Yes"), + min_size=(90,20), + max_size=(90,20)) + no_button = pychan.widgets.Button(name="no_button", + text=unicode("No"), + min_size=(90,20), + max_size=(90,20)) + + self.quit_window.addChild(label) + hbox.addChild(yes_button) + hbox.addChild(no_button) + self.quit_window.addChild(hbox) + + events_to_map = { "yes_button": self.quit_callback, + "no_button": self.quit_window.hide } + + self.quit_window.mapEvents(events_to_map) + + + def quitGame(self): + """Called when user requests to quit game. + @return: None""" + self.stopActions() + self.quit_window.show() + + def toggleInventoryButton(self): + """Manually toggles the inventory button. + @return: None""" + button = self.hud.findChild(name="inventoryButton") + if button.toggled == 0: + button.toggled = 1 + else: + button.toggled = 0 + + def toggleInventory(self, toggle_image=True): + """Displays the inventory screen + @return: None""" + if self.inventory == None: + self.initializeInventory() + self.inventory.toggleInventory(toggle_image) + + def toggleCharacterScreen(self): + if not self.character_screen: + self.initializeCharacterScreen() + if not self.character_screen.isVisible(): + self.character_screen.show() + else: + self.character_screen.hide() + + def refreshReadyImages(self): + """Make the Ready slot images on the HUD be the same as those + on the inventory + @return: None""" + for ready in range(1, 5): + button = self.hud.findChild(name=("hudReady%d" % ready)) + if self.inventory_storage == None : + origin = None + else: + origin = self.inventory_storage.getItemsInSlot('ready', ready-1) + if origin == None: + self.setImages(button, + self.inventory.slot_empty_images['ready']) + else: + self.setImages(button, origin.getInventoryThumbnail()) + + def setImages(self, widget, image): + """Set the up, down, and hover images of an Imagebutton. + @type widget: pychan.widget + @param widget: widget to set + @type image: string + @param image: image to use + @return: None""" + widget.up_image = image + widget.down_image = image + widget.hover_image = image + + def initializeEvents(self): + """Intialize Hud events + @return: None""" + events_to_map = {} + + # when we click the toggle button don't change the image + events_to_map["inventoryButton"] = cbwa(self.toggleInventory, False) + events_to_map["saveButton"] = self.saveGame + events_to_map["loadButton"] = self.loadGame + + hud_ready_buttons = ["hudReady1", "hudReady2", \ + "hudReady3", "hudReady4"] + + for item in hud_ready_buttons: + events_to_map[item] = cbwa(self.readyAction, item) + + self.hud.mapEvents(events_to_map) + + menu_events = {} + menu_events["newButton"] = self.newGame + menu_events["quitButton"] = self.quitGame + menu_events["saveButton"] = self.saveGame + menu_events["loadButton"] = self.loadGame + self.main_menu.mapEvents(menu_events) + + def readyAction(self, ready_button): + """ Called when the user selects a ready button from the HUD """ + text = "Used the item from %s" % ready_button + self.addAction(text) + + def createBoxGUI(self, title, container): + """Creates a window to display the contents of a box + @type title: string + @param title: The title for the window + @param items: The box to display + @return: A new ContainerGui""" + events = {'takeAllButton':self.hideContainer, + 'closeButton':self.hideContainer} + #hide previous container if any, to avoid orphaned dialogs + self.hideContainer() + + self.box_container = ContainerGUI(self.controller, + unicode(title), container) + self.box_container.gui.mapEvents(events) + self.box_container.showContainer() + return self.box_container + + def hideContainer(self): + """Hide the container box + @return: None""" + if self.box_container: + self.box_container.hideContainer() + #TODO: Move the close() call into OpenBoxAction(). This can be done + # after createBoxGUI becomes a blocking call or it's otherwise + # possible to wait till the box GUI is closed. + if self.box_container.container.trueAttr("openable"): + self.box_container.container.close() + self.box_container = None + + def createExamineBox(self, title, desc): + """Create an examine box. It displays some textual description of an + object + @type title: string + @param title: The title of the examine box + @type desc: string + @param desc: The main body of the examine box + @return: None""" + if self.examine_box is not None: + self.examine_box.closePopUp() + self.examine_box = ExaminePopup(self.engine, title, desc) + self.examine_box.showPopUp() + + def showDialogue(self, npc): + """Show the NPC dialogue window + @type npc: actors.NonPlayerCharacter + @param npc: the npc that we are having a dialogue with + @return: The dialogue""" + self.stopActions() + dialogue = DialogueGUI( + self.controller, + npc, + self.model.game_state.quest_engine, + self.model.game_state.player_character) + return dialogue