source: branches/active/character_customization/game/parpg/gui/hud.py @ 797

Revision 797, 19.6 KB checked in by aspidites, 9 years ago (diff)

Patch by Aspidites:

  • converted print statements to logging messages.
  • all of parpg's log messages go to the same file, but are differenciated by their class names
  • need to find a way to manipulate fife's log level
  • Property svn:eol-style set to native
Line 
1#   This file is part of PARPG.
2
3#   PARPG is free software: you can redistribute it and/or modify
4#   it under the terms of the GNU General Public License as published by
5#   the Free Software Foundation, either version 3 of the License, or
6#   (at your option) any later version.
7
8#   PARPG is distributed in the hope that it will be useful,
9#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11#   GNU General Public License for more details.
12
13#   You should have received a copy of the GNU General Public License
14#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
15
16from fife.extensions import pychan
17from fife.extensions.pychan.tools import callbackWithArguments as cbwa
18
19from parpg.gui.filebrowser import FileBrowser
20from parpg.gui.menus import ContextMenu, SettingsMenu
21from parpg.gui import inventorygui
22from parpg.gui.popups import ExaminePopup
23from parpg.gui.containergui import ContainerGUI
24from parpg.gui.dialoguegui import DialogueGUI
25from parpg.gui import drag_drop_data as data_drag
26from actionsbox import ActionsBox
27
28import logging
29
30logger = logging.getLogger('hud')
31
32class Hud(object):
33    """Main Hud class"""
34    def __init__(self, controller, settings, callbacks):
35        """Initialise the instance.
36           @type controller: Class derived from ControllerBase
37           @param controller: The current controller
38           @type settings: settings.Setting
39           @param settings: The settings
40           @type inv_model: dict
41           @type callbacks: dict
42           @param callbacks: a dict of callbacks
43               saveGame: called when the user clicks on Save
44               loadGame: called when the user clicks on Load
45               quitGame: called when the user clicks on Quit
46           @return: None"""
47
48        # TODO: perhaps this should not be hard-coded here
49        pychan.registerWidget(ActionsBox)
50        self.hud = pychan.loadXML("gui/hud.xml")
51        self.controller = controller
52        self.engine = controller.engine
53        self.model = controller.model
54        self.settings = settings
55        self.inventory = None
56        self.character_screen = None
57
58        self.save_game_callback = callbacks['saveGame']
59        self.load_game_callback = callbacks['loadGame']
60        self.quit_callback      = callbacks['quitGame']
61
62        self.box_container = None
63        self.examine_box = None
64        self.context_menu = None
65        self.help_dialog = None
66        self.events_to_map = None
67        self.main_menu = None
68        self.menu_events = None
69        self.quit_window = None
70        self.bottom_panel = self.hud.findChild(name="mainHudWindow")
71       
72        self.actions_box = self.hud.findChild(name="actionsBox")
73        self.menu_displayed = False
74        self.inventory_storage = None
75        self.initializeHud()
76        self.initializeMainMenu()
77        self.initializeContextMenu()
78        self.initializeHelpMenu()
79        self.initializeEvents()
80        self.initializeQuitDialog()
81        self.initializeSettingsMenu()
82   
83    def _getEnabled(self):
84        """"Returns whether the gui widget is enabled or not"""
85        return self.hud.real_widget.isEnabled()
86   
87    def _setEnabled(self, enabled):
88        """"Sets whether the gui widget is enabled or not"""
89        self.hud.real_widget.setEnabled(enabled)
90        childs = self.hud.getNamedChildren()
91        for child_list in childs.itervalues():
92            for child in child_list:
93                child.real_widget.setEnabled(enabled)
94   
95    enabled = property(_getEnabled, _setEnabled)
96       
97    def initializeHud(self):
98        """Initialize and show the main HUD
99           @return: None"""
100        self.events_to_map = {"menuButton":self.displayMenu, }
101        self.hud.mapEvents(self.events_to_map) 
102        # set HUD size according to screen size
103        screen_width = self.engine.getSettings().getScreenWidth()
104        self.hud.findChild(name="mainHudWindow").size = (screen_width, 65)
105        self.hud.findChild(name="inventoryButton").position = \
106                                                    (screen_width-59, 7)
107        # add ready slots
108        ready1 = self.hud.findChild(name='hudReady1')
109        ready2 = self.hud.findChild(name='hudReady2')
110        ready3 = self.hud.findChild(name='hudReady3')
111        ready4 = self.hud.findChild(name='hudReady4')
112
113        if (screen_width <=800) :
114            gap = 0
115        else :
116            gap = 40
117        ready1.position = (160+gap, 7)
118        ready2.position = (220+gap, 7)
119        ready3.position = (screen_width-180-gap, 7)
120        ready4.position = (screen_width-120-gap, 7)
121        self.actions_box.position = (280+gap, 5)
122        actions_width = screen_width - 470 - 2*gap
123
124        self.actions_box.ContentBox.min_width = actions_width
125        self.actions_box.ContentBox.max_width = actions_width
126       
127        # and finally add an actions box
128        self.actions_box.min_size = (actions_width, 55)
129        self.actions_box.max_size = (actions_width, 55)
130        # now it should be OK to display it all
131        self.showHUD()
132       
133    def addAction(self, action):
134        """Add an action to the actions box.
135           @type action: (unicode) string
136           @param action: The text that you want to display in the actions box
137           @return: None"""   
138        self.actions_box.addAction(action)
139
140    def showHUD(self):
141        """Show the HUD.
142           @return: None"""
143        self.hud.show()
144        self.enabled = True
145
146    def hideHUD(self):
147        """Hide the HUD.
148           @return: None"""
149        self.hud.hide()
150        self.enabled = False
151
152    def initializeInventory(self):
153        """Initialize the inventory"""
154        if not self.inventory:
155            self.inventory = inventorygui.InventoryGUI(self.controller,
156                                                       None,
157                                                       None)
158#        inv_callbacks = {
159#            'refreshReadyImages': self.refreshReadyImages,
160#            'toggleInventoryButton': self.toggleInventoryButton,
161#        }
162#        self.inventory_storage = \
163#            self.model.game_state.player_character.inventory
164#        if self.inventory == None:
165#            self.inventory = inventorygui.InventoryGUI(self.controller,
166#                                                       self.inventory_storage,
167#                                                       inv_callbacks)
168#        else:
169#            self.inventory.inventory_storage = self.inventory_storage
170#        self.refreshReadyImages()
171   
172    def initializeCharacterScreen(self):
173        """Initialize the character screen."""
174        # TODO Technomage 2010-12-24:
175        if not self.character_screen:
176            self.character_screen = pychan.loadXML('gui/character_screen.xml')
177       
178   
179    def initializeContextMenu(self):
180        """Initialize the Context Menu
181           @return: None"""
182        self.context_menu = ContextMenu (self.engine, [], (0, 0))
183
184    def showContextMenu(self, data, pos):
185        """Display the Context Menu with model at pos
186           @type model: list
187           @param model: model to pass to context menu
188           @type pos: tuple
189           @param pos: tuple of x and y coordinates
190           @return: None"""
191        self.context_menu = ContextMenu(self.engine, data, pos)
192        self.context_menu.show()
193
194    def hideContextMenu(self):
195        """Hides the context menu
196           @return: None"""
197        self.context_menu.hide()
198
199    def initializeMainMenu(self):
200        """Initalize the main menu.
201           @return: None"""
202        self.main_menu = pychan.loadXML("gui/hud_pause_menu.xml")
203        #TODO: find more suitalbe place for onOptilonsPress implementation
204        self.menu_events = {"resumeButton": self.hideMenu, 
205                            "settingsButton": self.displaySettings,
206                            "helpButton": self.displayHelp}
207        self.main_menu.mapEvents(self.menu_events)
208
209    def displayMenu(self):
210        """Displays the main in-game menu.
211           @return: None"""
212        self.stopActions()
213        if (self.menu_displayed == False):
214            self.main_menu.show()
215            self.menu_displayed = True
216            self.model.pause(True)
217            self.controller.pause(True)
218            self.enabled = False
219        elif (self.menu_displayed == True):
220            self.hideMenu()
221
222    def hideMenu(self):
223        """Hides the main in-game menu.
224           @return: None"""
225        self.main_menu.hide()
226        self.menu_displayed = False
227        self.model.pause(False)
228        self.controller.pause(False)
229        self.enabled = True
230
231    def initializeSettingsMenu(self):
232        self.settings_menu = SettingsMenu(self.settings)
233
234    def displaySettings(self):
235        self.settings_menu.show()
236
237    def initializeHelpMenu(self):
238        """Initialize the help menu
239           @return: None"""
240        self.help_dialog = pychan.loadXML("gui/help.xml")
241        help_events = {"closeButton":self.help_dialog.hide}
242        self.help_dialog.mapEvents(help_events)
243        main_help_text = u"Welcome to Post-Apocalyptic RPG or PARPG![br][br]"\
244        "This game is still in development, so please expect for there to be "\
245        "bugs and[br]feel free to tell us about them at "\
246        "http://www.forums.parpg.net.[br]This game uses a "\
247        "\"Point 'N' Click\" interface, which means that to move around,[br]"\
248        "just click where you would like to go and your character will move "\
249        "there.[br]PARPG also utilizes a context menu. To access this, just "\
250        "right click anywhere[br]on the screen and a menu will come up. This "\
251        "menu will change depending on[br]what you have clicked on, hence "\
252        "it's name \"context menu\".[br][br]"
253       
254        k_text = u" Keybindings" 
255        k_text += "[br] A : Add a test action to the actions display"
256        k_text += "[br] I : Toggle the inventory screen"
257        k_text += "[br] F5 : Take a screenshot"
258        k_text += "[br]      (saves to <parpg>/screenshots/)"
259        k_text += "[br] F10 : Toggle console"
260        k_text += "[br] PAUSE : (Un)Pause the game"
261        k_text += "[br] Q : Quit the game"
262        self.help_dialog.distributeInitialData({
263                "MainHelpText":main_help_text,
264                "KeybindText":k_text
265                })
266
267    def displayHelp(self):
268        """Display the help screen.
269           @return: None"""
270        self.help_dialog.show()
271
272    def saveGame(self):
273        """ Called when the user wants to save the game.
274            @return: None"""
275        self.stopActions()
276        save_browser = FileBrowser(self.engine,
277                                   self.save_game_callback,
278                                   self.loadsave_close,
279                                   save_file=True,
280                                   gui_xml_path="gui/savebrowser.xml",
281                                   extensions = ('.dat'))
282        save_browser.showBrowser()
283        self.controller.pause(True)
284        self.model.pause(True)
285        self.enabled = False
286
287    def stopActions(self):
288        """This method stops/resets actions that are currently performed
289        like dragging an item.
290        This is done to be able to savely perform other actions that might
291        interfere with current running ones."""
292        #Reset dragging - move item back to its old container
293        if data_drag.dragging:
294            data_drag.source_container.placeItem(data_drag.dragged_item)
295            data_drag.dragging = False
296            data_drag.dragged_item = None
297        if self.inventory:
298            self.inventory.closeInventory()
299       
300    def newGame(self):
301        """Called when user request to start a new game.
302           @return: None"""
303        self.stopActions()
304        logger.info('new game')
305   
306    def loadsave_close(self):
307        """Called when the load/save filebrowser was closed without a file selected"""
308        if not self.menu_displayed:
309            self.enabled = True
310            self.model.pause(False)
311            self.controller.pause(False)
312           
313    def loadGame(self):
314        """ Called when the user wants to load a game.
315            @return: None"""
316        self.stopActions()
317        load_browser = FileBrowser(self.engine,
318                                   self.load_game_callback,
319                                   close_callback = self.loadsave_close,
320                                   save_file=False,
321                                   gui_xml_path='gui/loadbrowser.xml',
322                                   extensions=('.dat'))
323        load_browser.showBrowser()
324        self.model.pause(True)
325        self.controller.pause(True)
326        self.enabled = False
327   
328    def initializeQuitDialog(self):
329        """Creates the quit confirmation dialog
330           @return: None"""
331        self.quit_window = pychan.widgets.Window(title=unicode("Quit?"), \
332                                                 min_size=(200,0))
333
334        hbox = pychan.widgets.HBox()
335        are_you_sure = "Are you sure you want to quit?"
336        label = pychan.widgets.Label(text=unicode(are_you_sure))
337        yes_button = pychan.widgets.Button(name="yes_button", 
338                                           text=unicode("Yes"),
339                                           min_size=(90,20),
340                                           max_size=(90,20))
341        no_button = pychan.widgets.Button(name="no_button",
342                                          text=unicode("No"),
343                                          min_size=(90,20),
344                                          max_size=(90,20))
345
346        self.quit_window.addChild(label)
347        hbox.addChild(yes_button)
348        hbox.addChild(no_button)
349        self.quit_window.addChild(hbox)
350
351        events_to_map = { "yes_button": self.quit_callback,
352                          "no_button":  self.quit_window.hide }
353       
354        self.quit_window.mapEvents(events_to_map)
355
356
357    def quitGame(self):
358        """Called when user requests to quit game.
359           @return: None"""
360        self.stopActions()
361        self.quit_window.show()
362
363    def toggleInventoryButton(self):
364        """Manually toggles the inventory button.
365           @return: None"""
366        button = self.hud.findChild(name="inventoryButton")
367        if button.toggled == 0:
368            button.toggled = 1
369        else:
370            button.toggled = 0
371
372    def toggleInventory(self, toggle_image=True):
373        """Displays the inventory screen
374           @return: None"""
375        if self.inventory == None:
376            self.initializeInventory()
377        self.inventory.toggleInventory(toggle_image)
378   
379    def toggleCharacterScreen(self):
380        if not self.character_screen:
381            self.initializeCharacterScreen()
382        if not self.character_screen.isVisible():
383            self.character_screen.show()
384        else:
385            self.character_screen.hide()
386   
387    def refreshReadyImages(self):
388        """Make the Ready slot images on the HUD be the same as those
389           on the inventory
390           @return: None"""
391        for ready in range(1, 5):
392            button = self.hud.findChild(name=("hudReady%d" % ready))
393            if self.inventory_storage == None :
394                origin = None
395            else:
396                origin = self.inventory_storage.getItemsInSlot('ready', ready-1)
397            if origin == None:
398                self.setImages(button, 
399                               self.inventory.slot_empty_images['ready'])
400            else:
401                self.setImages(button, origin.getInventoryThumbnail())
402
403    def setImages(self, widget, image):
404        """Set the up, down, and hover images of an Imagebutton.
405           @type widget: pychan.widget
406           @param widget: widget to set
407           @type image: string
408           @param image: image to use
409           @return: None"""
410        widget.up_image = image
411        widget.down_image = image
412        widget.hover_image = image
413
414    def initializeEvents(self):
415        """Intialize Hud events
416           @return: None"""
417        events_to_map = {}
418
419        # when we click the toggle button don't change the image
420        events_to_map["inventoryButton"] = cbwa(self.toggleInventory, False)
421        events_to_map["saveButton"] = self.saveGame
422        events_to_map["loadButton"] = self.loadGame
423
424        hud_ready_buttons = ["hudReady1", "hudReady2", \
425                             "hudReady3", "hudReady4"]
426
427        for item in hud_ready_buttons:
428            events_to_map[item] = cbwa(self.readyAction, item)
429
430        self.hud.mapEvents(events_to_map)
431
432        menu_events = {}
433        menu_events["newButton"] = self.newGame
434        menu_events["quitButton"] = self.quitGame
435        menu_events["saveButton"] = self.saveGame
436        menu_events["loadButton"] = self.loadGame
437        self.main_menu.mapEvents(menu_events)
438
439    def setOption(self, name, value):
440        """Set an option within the xml.
441           @type name: string
442           @param name: The name of the option within the xml
443           @type value: any
444           @param value: The value that the option 'name' should be set to
445           @return: None"""
446        element = self.settings.root_element.find(name)
447        if(element != None):
448            if(value != element.text):
449                element.text = str(value)
450        else:
451            logger.warning("setting, {0} does not exist!".format(name))
452   
453    def readyAction(self, ready_button):
454        """ Called when the user selects a ready button from the HUD """
455        text = "Used the item from %s" % ready_button       
456        self.addAction(text)
457       
458    def createBoxGUI(self, title, container):
459        """Creates a window to display the contents of a box
460           @type title: string
461           @param title: The title for the window
462           @param items: The box to display
463           @return: A new ContainerGui"""
464        events = {'takeAllButton':self.hideContainer,
465                  'closeButton':self.hideContainer}
466        #hide previous container if any, to avoid orphaned dialogs
467        self.hideContainer()
468
469        self.box_container = ContainerGUI(self.controller,
470                                              unicode(title), container)
471        self.box_container.gui.mapEvents(events)
472        self.box_container.showContainer()
473        return self.box_container
474
475    def hideContainer(self):
476        """Hide the container box
477           @return: None"""
478        if self.box_container:
479            self.box_container.hideContainer()
480            #TODO: Move the close() call into OpenBoxAction(). This can be done
481            # after createBoxGUI becomes a blocking call or it's otherwise
482            # possible to wait till the box GUI is closed.
483            if self.box_container.container.trueAttr("openable"):
484                self.box_container.container.close()
485            self.box_container = None
486
487    def createExamineBox(self, title, desc):
488        """Create an examine box. It displays some textual description of an
489           object
490           @type title: string
491           @param title: The title of the examine box
492           @type desc: string
493           @param desc: The main body of the examine box
494           @return: None"""
495        if self.examine_box is not None:
496            self.examine_box.closePopUp()
497        self.examine_box = ExaminePopup(self.engine, title, desc)
498        self.examine_box.showPopUp()
499
500    def showDialogue(self, npc):
501        """Show the NPC dialogue window
502           @type npc: actors.NonPlayerCharacter
503           @param npc: the npc that we are having a dialogue with
504           @return: The dialogue"""
505        self.stopActions()
506        dialogue = DialogueGUI(
507                    self.controller,
508                    npc,
509                    self.model.game_state.quest_engine,
510                    self.model.game_state.player_character)
511        return dialogue
Note: See TracBrowser for help on using the repository browser.