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

Revision 810, 19.5 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspidites

  • Removed SystemDataDirectory? and UserDataDirectory?
    • it is the the settings module's job to keep track of this sort of thing, not fife or PARPG's
    • consequently, setting.py's command line interface became simple
    • this will also prevent those "I generated a settings file but PARPG doesnt run" complaints
    • I think I'm going to have some helper methods to generate platform-specific paths at run-time
  • User data directory is now properly created
  • added platform_paths(system) which returns the platform-specific paths for the given system
  • if no system is given, it gives the paths for the system that the script was run on
  • changed logic of settings.py so that it is not required to pass a path.
  • not passing a path invokes paltform_paths
  • 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
16import os
17import logging
18
19from fife.extensions import pychan
20from fife.extensions.pychan.tools import callbackWithArguments as cbwa
21
22from parpg.gui.filebrowser import FileBrowser
23from parpg.gui.menus import ContextMenu, SettingsMenu
24from parpg.gui import inventorygui
25from parpg.gui.popups import ExaminePopup
26from parpg.gui.containergui import ContainerGUI
27from parpg.gui.dialoguegui import DialogueGUI
28from parpg.gui import drag_drop_data as data_drag
29from actionsbox import ActionsBox
30
31logger = logging.getLogger('hud')
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.engine, 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] F7 : Take a screenshot"
258        k_text += "[br]      (Saves to screenshots directory)"
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        xml_path = os.path.join(self.settings.paths['system'],
277                                    self.settings.parpg.GuiDirectory,
278                                    'savebrowser.xml')
279        save_browser = FileBrowser(self.engine,
280                                   self.settings,
281                                   self.save_game_callback,
282                                   xml_path,
283                                   self.loadsave_close,
284                                   save_file=True,
285                                   extensions=('.dat'))
286        save_browser.showBrowser()
287        self.controller.pause(True)
288        self.model.pause(True)
289        self.enabled = False
290
291    def stopActions(self):
292        """This method stops/resets actions that are currently performed
293        like dragging an item.
294        This is done to be able to savely perform other actions that might
295        interfere with current running ones."""
296        #Reset dragging - move item back to its old container
297        if data_drag.dragging:
298            data_drag.source_container.placeItem(data_drag.dragged_item)
299            data_drag.dragging = False
300            data_drag.dragged_item = None
301        if self.inventory:
302            self.inventory.closeInventory()
303       
304    def newGame(self):
305        """Called when user request to start a new game.
306           @return: None"""
307        self.stopActions()
308        logger.info('new game')
309   
310    def loadsave_close(self):
311        """Called when the load/save filebrowser was closed without a file selected"""
312        if not self.menu_displayed:
313            self.enabled = True
314            self.model.pause(False)
315            self.controller.pause(False)
316           
317    def loadGame(self):
318        """ Called when the user wants to load a game.
319            @return: None"""
320        self.stopActions()
321        xml_path = os.path.join(self.settings.paths['system'],
322                                    self.settings.parpg.GuiDirectory,
323                                    'loadbrowser.xml')
324        load_browser = FileBrowser(self.engine,
325                                   self.settings,
326                                   self.load_game_callback,
327                                   xml_path,
328                                   close_callback = self.loadsave_close,
329                                   save_file=False,
330                                   extensions=('.dat'))
331        load_browser.showBrowser()
332        self.model.pause(True)
333        self.controller.pause(True)
334        self.enabled = False
335   
336    def initializeQuitDialog(self):
337        """Creates the quit confirmation dialog
338           @return: None"""
339        self.quit_window = pychan.widgets.Window(title=unicode("Quit?"), \
340                                                 min_size=(200,0))
341
342        hbox = pychan.widgets.HBox()
343        are_you_sure = "Are you sure you want to quit?"
344        label = pychan.widgets.Label(text=unicode(are_you_sure))
345        yes_button = pychan.widgets.Button(name="yes_button", 
346                                           text=unicode("Yes"),
347                                           min_size=(90,20),
348                                           max_size=(90,20))
349        no_button = pychan.widgets.Button(name="no_button",
350                                          text=unicode("No"),
351                                          min_size=(90,20),
352                                          max_size=(90,20))
353
354        self.quit_window.addChild(label)
355        hbox.addChild(yes_button)
356        hbox.addChild(no_button)
357        self.quit_window.addChild(hbox)
358
359        events_to_map = { "yes_button": self.quit_callback,
360                          "no_button":  self.quit_window.hide }
361       
362        self.quit_window.mapEvents(events_to_map)
363
364
365    def quitGame(self):
366        """Called when user requests to quit game.
367           @return: None"""
368        self.stopActions()
369        self.quit_window.show()
370
371    def toggleInventoryButton(self):
372        """Manually toggles the inventory button.
373           @return: None"""
374        button = self.hud.findChild(name="inventoryButton")
375        if button.toggled == 0:
376            button.toggled = 1
377        else:
378            button.toggled = 0
379
380    def toggleInventory(self, toggle_image=True):
381        """Displays the inventory screen
382           @return: None"""
383        if self.inventory == None:
384            self.initializeInventory()
385        self.inventory.toggleInventory(toggle_image)
386   
387    def toggleCharacterScreen(self):
388        if not self.character_screen:
389            self.initializeCharacterScreen()
390        if not self.character_screen.isVisible():
391            self.character_screen.show()
392        else:
393            self.character_screen.hide()
394   
395    def refreshReadyImages(self):
396        """Make the Ready slot images on the HUD be the same as those
397           on the inventory
398           @return: None"""
399        for ready in range(1, 5):
400            button = self.hud.findChild(name=("hudReady%d" % ready))
401            if self.inventory_storage == None :
402                origin = None
403            else:
404                origin = self.inventory_storage.getItemsInSlot('ready', ready-1)
405            if origin == None:
406                self.setImages(button, 
407                               self.inventory.slot_empty_images['ready'])
408            else:
409                self.setImages(button, origin.getInventoryThumbnail())
410
411    def setImages(self, widget, image):
412        """Set the up, down, and hover images of an Imagebutton.
413           @type widget: pychan.widget
414           @param widget: widget to set
415           @type image: string
416           @param image: image to use
417           @return: None"""
418        widget.up_image = image
419        widget.down_image = image
420        widget.hover_image = image
421
422    def initializeEvents(self):
423        """Intialize Hud events
424           @return: None"""
425        events_to_map = {}
426
427        # when we click the toggle button don't change the image
428        events_to_map["inventoryButton"] = cbwa(self.toggleInventory, False)
429        events_to_map["saveButton"] = self.saveGame
430        events_to_map["loadButton"] = self.loadGame
431
432        hud_ready_buttons = ["hudReady1", "hudReady2", \
433                             "hudReady3", "hudReady4"]
434
435        for item in hud_ready_buttons:
436            events_to_map[item] = cbwa(self.readyAction, item)
437
438        self.hud.mapEvents(events_to_map)
439
440        menu_events = {}
441        menu_events["newButton"] = self.newGame
442        menu_events["quitButton"] = self.quitGame
443        menu_events["saveButton"] = self.saveGame
444        menu_events["loadButton"] = self.loadGame
445        self.main_menu.mapEvents(menu_events)
446
447    def readyAction(self, ready_button):
448        """ Called when the user selects a ready button from the HUD """
449        text = "Used the item from %s" % ready_button       
450        self.addAction(text)
451       
452    def createBoxGUI(self, title, container):
453        """Creates a window to display the contents of a box
454           @type title: string
455           @param title: The title for the window
456           @param items: The box to display
457           @return: A new ContainerGui"""
458        events = {'takeAllButton':self.hideContainer,
459                  'closeButton':self.hideContainer}
460        #hide previous container if any, to avoid orphaned dialogs
461        self.hideContainer()
462
463        self.box_container = ContainerGUI(self.controller,
464                                              unicode(title), container)
465        self.box_container.gui.mapEvents(events)
466        self.box_container.showContainer()
467        return self.box_container
468
469    def hideContainer(self):
470        """Hide the container box
471           @return: None"""
472        if self.box_container:
473            self.box_container.hideContainer()
474            #TODO: Move the close() call into OpenBoxAction(). This can be done
475            # after createBoxGUI becomes a blocking call or it's otherwise
476            # possible to wait till the box GUI is closed.
477            if self.box_container.container.trueAttr("openable"):
478                self.box_container.container.close()
479            self.box_container = None
480
481    def createExamineBox(self, title, desc):
482        """Create an examine box. It displays some textual description of an
483           object
484           @type title: string
485           @param title: The title of the examine box
486           @type desc: string
487           @param desc: The main body of the examine box
488           @return: None"""
489        if self.examine_box is not None:
490            self.examine_box.closePopUp()
491        self.examine_box = ExaminePopup(self.engine, title, desc)
492        self.examine_box.showPopUp()
493
494    def showDialogue(self, npc):
495        """Show the NPC dialogue window
496           @type npc: actors.NonPlayerCharacter
497           @param npc: the npc that we are having a dialogue with
498           @return: The dialogue"""
499        self.stopActions()
500        dialogue = DialogueGUI(
501                    self.controller,
502                    npc,
503                    self.model.game_state.quest_engine,
504                    self.model.game_state.player_character)
505        return dialogue
Note: See TracBrowser for help on using the repository browser.