source: branches/active/character_customization/game/scripts/gui/hud.py @ 726

Revision 726, 19.3 KB checked in by technomage, 9 years ago (diff)

Patch by Technomage

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