source: trunk/game/scripts/hud.py @ 342

Revision 326, 22.0 KB checked in by eliedebrauwer, 10 years ago (diff)

Ticket #117: Patch by eliedebrauwer, fixed load/save game callback, due to refactoring (see ticket #2) variable and function names got mixed up. fixes[s:trac, t:117]

  • Property svn:eol-style set to native
Line 
1#!/usr/bin/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
18import shutil
19import fife
20import pychan
21import pychan.widgets as widgets
22from pychan.tools import callbackWithArguments as cbwa
23from scripts.filebrowser import FileBrowser
24from scripts.context_menu import ContextMenu
25from scripts import inventory
26from scripts.popups import ExaminePopup, ContainerGUI
27from scripts.dialoguegui import DialogueGUI
28
29class Hud(object):
30    """Main Hud class"""
31    def __init__(self, engine, settings, inv_model, callbacks):
32        """Initialise the instance.
33           @type engine: fife.Engine
34           @param engine: An instance of the fife engine
35           @type settings: settings.Setting
36           @param settings: The settings data
37           @type inv_model: dict
38           @type callbacks: dict
39           @param callbacks: a dict of callbacks
40               saveGame: called when the user clicks on Save
41               loadGame: called when the user clicks on Load
42               quitGame: called when the user clicks on Quit
43           @return: None"""
44        pychan.init(engine, debug = True)
45
46        # TODO: perhaps this should not be hard-coded here
47        self.hud = pychan.loadXML("gui/hud.xml")
48        self.engine = engine
49        self.settings = settings
50
51        inv_callbacks = {
52            'refreshReadyImages': self.refreshReadyImages,
53            'toggleInventoryButton': self.toggleInventoryButton,
54        }
55
56        self.inventory = inventory.Inventory(self.engine, \
57                                             inv_model, inv_callbacks)
58        self.refreshReadyImages()
59
60        self.save_game_callback = callbacks['saveGame']
61        self.load_game_callback = callbacks['loadGame']
62        self.quit_callback      = callbacks['quitGame']
63
64        self.box_container = None
65        self.examine_box = None
66
67        self.actions_box = self.hud.findChild(name="actionsBox")
68        self.actions_text = []
69        self.menu_displayed = False
70        self.initializeHud()
71        self.initializeMainMenu()
72        self.initializeContextMenu()
73        self.initializeOptionsMenu()
74        self.initializeHelpMenu()
75        self.initializeEvents()
76        self.initializeQuitDialog()
77
78    def initializeHud(self):
79        """Initialize and show the main HUD
80           @return: None"""
81        self.events_to_map = {"menuButton":self.displayMenu,}
82        self.hud.mapEvents(self.events_to_map) 
83        # set HUD size accoriding to screen size
84        screen_width = int(self.settings.readSetting('ScreenWidth'))
85        self.hud.findChild(name="mainHudWindow").size = (screen_width, 65)
86        self.hud.findChild(name="inventoryButton").position = \
87                                                    (screen_width-59, 7)
88        # add ready slots
89        ready1 = self.hud.findChild(name='hudReady1')
90        ready2 = self.hud.findChild(name='hudReady2')
91        ready3 = self.hud.findChild(name='hudReady3')
92        ready4 = self.hud.findChild(name='hudReady4')
93        actions_scroll_area = self.hud.findChild(name='actionsScrollArea')
94        if (screen_width <=800) :
95            gap = 0
96        else :
97            gap = 40
98        # annoying code that is both essential and boring to enter
99        ready1.position = (160+gap, 7)
100        ready2.position = (220+gap, 7)
101        ready3.position = (screen_width-180-gap, 7)
102        ready4.position = (screen_width-120-gap, 7)
103        actions_scroll_area.position = (280+gap, 5)
104        actions_width = screen_width - 470 - 2*gap
105
106        # and finally add an actions box
107        self.hud.findChild(name="actionsBox").min_size = (actions_width, 0)
108        actions_scroll_area.min_size = (actions_width, 55)
109        actions_scroll_area.max_size = (actions_width, 55)
110        # now it should be OK to display it all
111        self.hud.show()
112
113    def refreshActionsBox(self):
114        """Refresh the actions box so that it displays the contents of
115           self.actions_text
116           @return: None"""
117        self.actions_box.items = self.actions_text
118
119    def addAction(self, action):
120        """Add an action to the actions box.
121           @type action: string
122           @param action: The text that you want to display in the actions box
123           @return: None"""
124        self.actions_text.insert(0, action)
125        self.refreshActionsBox()
126
127    def showHUD(self):
128        """Show the HUD.
129           @return: None"""
130        self.hud.show()
131
132    def hideHUD(self):
133        """Hide the HUD.
134           @return: None"""
135        self.hud.hide()
136
137    def initializeContextMenu(self):
138        """Initialize the Context Menu
139           @return: None"""
140        self.context_menu = ContextMenu (self.engine, [], (0,0))
141        self.context_menu.hide()
142
143    def showContextMenu(self, data, pos):
144        """Display the Context Menu with data at pos
145           @type data: list
146           @param data: data to pass to context menu
147           @type pos: tuple
148           @param pos: tuple of x and y coordinates
149           @return: None"""
150        self.context_menu = ContextMenu(self.engine, data, pos)
151
152    def hideContextMenu(self):
153        """Hides the context menu
154           @return: None"""
155        self.context_menu.hide()
156
157    def initializeMainMenu(self):
158        """Initalize the main menu.
159           @return: None"""
160        self.main_menu = pychan.loadXML("gui/hud_main_menu.xml")
161        self.menu_events = {"resumeButton":self.hideMenu, 
162                            "optionsButton":self.displayOptions,
163                            "helpButton":self.displayHelp}
164        self.main_menu.mapEvents(self.menu_events)
165
166    def displayMenu(self):
167        """Displays the main in-game menu.
168           @return: None"""
169        if (self.menu_displayed == False):
170            self.main_menu.show()
171            self.menu_displayed = True
172        elif (self.menu_displayed == True):
173            self.hideMenu()
174
175    def hideMenu(self):
176        """Hides the main in-game menu.
177           @return: None"""
178        self.main_menu.hide()
179        self.menu_displayed = False
180
181
182    def initializeHelpMenu(self):
183        """Initialize the help menu
184           @return: None"""
185        self.help_dialog = pychan.loadXML("gui/help.xml")
186        help_events = {"closeButton":self.help_dialog.hide}
187        self.help_dialog.mapEvents(help_events)
188        main_help_text = u"Welcome to Post-Apocalyptic RPG or PARPG![br][br]"\
189        "This game is still in development, so please expect for there to be "\
190        "bugs and[br]feel free to tell us about them at "\
191        "http://www.forums.parpg.net.[br]This game uses a "\
192        "\"Point 'N' Click\" interface, which means that to move around,[br]"\
193        "just click where you would like to go and your character will move "\
194        "there.[br]PARPG also utilizes a context menu. To access this, just "\
195        "right click anywhere[br]on the screen and a menu will come up. This "\
196        "menu will change depending on[br]what you have clicked on, hence "\
197        "it's name \"context menu\".[br][br]"
198       
199        k_text = u" Keybindings" 
200        k_text += "[br] A : Add a test action to the actions display"
201        k_text += "[br] I : Toggle the inventory screen"
202        k_text += "[br] F5 : Take a screenshot"
203        k_text += "[br]      (saves to <parpg>/screenshots/)"
204        k_text += "[br] Q : Quit the game"
205        self.help_dialog.distributeInitialData({
206                "MainHelpText":main_help_text,
207                "KeybindText":k_text
208                })
209
210    def displayHelp(self):
211        """Display the help screen.
212           @return: None"""
213        self.help_dialog.show()
214
215    def initializeOptionsMenu(self):
216        """Initalize the options menu.
217           @return: None"""
218        self.options_menu = pychan.loadXML("gui/hud_options.xml")
219        self.options_events = {"applyButton":self.applyOptions,
220                               "closeButton":self.options_menu.hide,
221                               "defaultsButton":self.setToDefaults,
222                               "InitialVolumeSlider":self.updateVolumeText}
223       
224        settings = self.engine.getSettings()
225        current_fullscreen = settings.isFullScreen()
226        settings.setFullScreen(True)
227        available_resolutions = settings.getPossibleResolutions()
228
229        # Filter too small resolutions
230        self.resolutions=[]
231        for x in available_resolutions:
232            if x[0]>=1024 and x[1]>=768:
233                self.resolutions.append(str(x[0])+'x'+str(x[1]))
234
235        settings.setFullScreen(current_fullscreen)
236        self.render_backends = ['OpenGL', 'SDL']
237        self.render_number = 0
238        if (str(self.settings.readSetting('RenderBackend')) == "SDL"):
239            self.render_number = 1
240        initial_volume = float(self.settings.readSetting('InitialVolume'))
241        initial_volume_text = str('Initial Volume: %.0f%s' %
242                                (int(initial_volume*10), "%"))
243        self.options_menu.distributeInitialData({
244                'ResolutionBox': self.resolutions,
245                'RenderBox': self.render_backends,
246                'InitialVolumeLabel' : initial_volume_text
247                })
248
249        s_fullscreen = self.settings.readSetting(name="FullScreen")
250        s_sounds = self.settings.readSetting(name="PlaySounds")
251        s_render = self.render_number
252        s_volume = initial_volume
253
254        screen_width = self.settings.readSetting(name="ScreenWidth")
255        screen_height = self.settings.readSetting(name="ScreenHeight")
256        index_res = str(screen_width + 'x' + screen_height)
257        try:
258            s_resolution = self.resolutions.index(index_res)
259            resolution_in_list = True
260        except:
261            resolution_in_list = False
262
263        data_to_distribute = {
264                'FullscreenBox':int(s_fullscreen), 
265                'SoundsBox':int(s_sounds),
266                'RenderBox': s_render,
267                'InitialVolumeSlider':s_volume
268                }
269
270        if (resolution_in_list == True):
271            data_to_distribute['ResolutionBox'] = s_resolution
272
273        self.options_menu.distributeData(data_to_distribute)
274
275        self.options_menu.mapEvents(self.options_events)
276
277    def saveGame(self):
278        """ Called when the user wants to save the game.
279            @return: None"""
280        save_browser = FileBrowser(self.engine,
281                                   self.save_game_callback,
282                                   save_file=True,
283                                   gui_xml_path="gui/savebrowser.xml",
284                                   extensions = ('.dat'))
285        save_browser.showBrowser()
286           
287    def newGame(self):
288        """Called when user request to start a new game.
289           @return: None"""
290        print 'new game'
291
292    def loadGame(self):
293        """ Called when the user wants to load a game.
294            @return: None"""
295        load_browser = FileBrowser(self.engine,
296                                   self.load_game_callback,
297                                   save_file=False,
298                                   gui_xml_path='gui/loadbrowser.xml',
299                                   extensions=('.dat'))
300        load_browser.showBrowser()
301   
302    def initializeQuitDialog(self):
303        """Creates the quit confirmation dialog
304           @return: None"""
305        self.quit_window = pychan.widgets.Window(title=unicode("Quit?"), \
306                                                 min_size=(200,0))
307
308        hbox = pychan.widgets.HBox()
309        are_you_sure = "Are you sure you want to quit?"
310        label = pychan.widgets.Label(text=unicode(are_you_sure))
311        yes_button = pychan.widgets.Button(name="yes_button", 
312                                           text=unicode("Yes"),
313                                           min_size=(90,20),
314                                           max_size=(90,20))
315        no_button = pychan.widgets.Button(name="no_button",
316                                          text=unicode("No"),
317                                          min_size=(90,20),
318                                          max_size=(90,20))
319
320        self.quit_window.addChild(label)
321        hbox.addChild(yes_button)
322        hbox.addChild(no_button)
323        self.quit_window.addChild(hbox)
324
325        events_to_map = { "yes_button": self.quit_callback,
326                          "no_button":  self.quit_window.hide }
327       
328        self.quit_window.mapEvents(events_to_map)
329
330
331    def quitGame(self):
332        """Called when user requests to quit game.
333           @return: None"""
334
335        self.quit_window.show()
336
337    def toggleInventoryButton(self):
338        """Manually toggles the inventory button.
339           @return: None"""
340        button = self.hud.findChild(name="inventoryButton")
341        if button.toggled == 0:
342            button.toggled = 1
343        else:
344            button.toggled = 0
345
346    def toggleInventory(self, toggleImage=True):
347        """Display's the inventory screen
348           @return: None"""
349
350        self.inventory.toggleInventory(toggleImage)
351
352    def refreshReadyImages(self):
353        """Make the Ready slot images on the HUD be the same as those
354           on the inventory
355           @return: None"""
356        self.setImages(self.hud.findChild(name="hudReady1"),
357                       self.inventory.getImage("Ready1").up_image)
358
359        self.setImages(self.hud.findChild(name="hudReady2"),
360                       self.inventory.getImage("Ready2").up_image)
361
362        self.setImages(self.hud.findChild(name="hudReady3"),
363                       self.inventory.getImage("Ready3").up_image)
364
365        self.setImages(self.hud.findChild(name="hudReady4"),
366                       self.inventory.getImage("Ready4").up_image)
367
368    def setImages(self, widget, image):
369        """Set the up, down, and hover images of an Imagebutton.
370           @type widget: pychan.widget
371           @param widget: widget to set
372           @type image: string
373           @param image: image to use
374           @return: None"""
375        widget.up_image = image
376        widget.down_image = image
377        widget.hover_image = image
378
379    def initializeEvents(self):
380        """Intialize Hud events
381           @return: None"""
382        events_to_map = {}
383
384        # when we click the toggle button don't change the image
385        events_to_map["inventoryButton"] = cbwa(self.toggleInventory, False)
386        events_to_map["saveButton"] = self.saveGame
387        events_to_map["loadButton"] = self.loadGame
388
389        hud_ready_buttons = ["hudReady1", "hudReady2", \
390                             "hudReady3", "hudReady4"]
391
392        for item in hud_ready_buttons:
393            events_to_map[item] = cbwa(self.readyAction, item)
394
395        self.hud.mapEvents(events_to_map)
396
397        menu_events = {}
398        menu_events["newButton"] = self.newGame
399        menu_events["quitButton"] = self.quitGame
400        menu_events["saveButton"] = self.saveGame
401        menu_events["loadButton"] = self.loadGame
402        self.main_menu.mapEvents(menu_events)
403
404    def updateVolumeText(self):
405        """
406        Update the initial volume label to reflect the value of the slider
407        """
408        volume = float(self.options_menu.collectData("InitialVolumeSlider"))
409        volume_label = self.options_menu.findChild(name="InitialVolumeLabel")
410        volume_label.text = unicode("Initial Volume: %.0f%s" %
411                                    (int(volume*10), "%"))
412
413    def requireRestartDialog(self):
414        """Show a dialog asking the user to restart PARPG in order for their
415           changes to take effect.
416           @return: None"""
417        require_restart_dialog = pychan.loadXML('gui/hud_require_restart.xml')
418        require_restart_dialog.mapEvents(\
419                                {'okButton':require_restart_dialog.hide})
420        require_restart_dialog.show()
421
422    def applyOptions(self):
423        """Apply the current options.
424           @return: None"""
425        # At first no restart is required
426        self.require_restart = False
427
428        # get the current values of each setting from the options menu
429        enable_fullscreen = self.options_menu.collectData('FullscreenBox')
430        enable_sound = self.options_menu.collectData('SoundsBox')
431        screen_resolution = self.options_menu.collectData('ResolutionBox')
432        partition = self.resolutions[screen_resolution].partition('x')
433        screen_width = partition[0]
434        screen_height = partition[2]
435        render_backend = self.options_menu.collectData('RenderBox')
436        initial_volume = self.options_menu.collectData('InitialVolumeSlider')
437        initial_volume = "%.1f" % initial_volume
438
439        # get the options that are being used right now from settings.xml
440        s_fullscreen = self.settings.readSetting('FullScreen')
441        s_sounds = self.settings.readSetting('PlaySounds')
442        s_render = self.settings.readSetting('RenderBackend')
443        s_volume = self.settings.readSetting('InitialVolume')
444
445        s_screen_height = self.settings.readSetting('ScreenHeight')
446        s_screen_width = self.settings.readSetting('ScreenWidth')
447        s_resolution = s_screen_width + 'x' + s_screen_height
448
449        # On each:
450        # - Check to see whether the option within the xml matches the
451        #   option within the options menu
452        # - If they do not match, set the option within the xml to
453        #   to be what is within the options menu
454        # - Require a restart
455
456        if (int(enable_fullscreen) != int(s_fullscreen)):
457            self.setOption('FullScreen', int(enable_fullscreen))
458            self.require_restart = True
459           
460        if (int(enable_sound) != int(s_sounds)):
461            self.setOption('PlaySounds', int(enable_sound))
462            self.require_restart = True
463
464        if (screen_resolution != s_resolution):
465            self.setOption('ScreenWidth', int(screen_width))
466            self.setOption('ScreenHeight', int(screen_height))
467            self.require_restart = True
468
469        # Convert the number from the list of render backends to
470        # the string that FIFE wants for its settings.xml
471        if (render_backend == 0):
472            render_backend = 'OpenGL'
473        else:
474            render_backend = 'SDL'
475
476        if (render_backend != str(s_render)):
477            self.setOption('RenderBackend', render_backend)
478            self.require_restart = True
479
480        if (initial_volume != float(s_volume)):
481            self.setOption('InitialVolume', initial_volume)
482            self.require_restart = True
483       
484        # Write all the settings to settings.xml
485        self.settings.tree.write('settings.xml')
486       
487        # If the changes require a restart, popup the dialog telling
488        # the user to do so
489        if (self.require_restart):
490            self.requireRestartDialog()
491        # Once we are done, we close the options menu
492        self.options_menu.hide()
493
494    def setOption(self, name, value):
495        """Set an option within the xml.
496           @type name: string
497           @param name: The name of the option within the xml
498           @type value: any
499           @param value: The value that the option 'name' should be set to
500           @return: None"""
501        element = self.settings.root_element.find(name)
502        if(element != None):
503            if(value != element.text):
504                element.text = str(value)
505        else:
506            print 'Setting,', name, 'does not exist!'
507
508    def setToDefaults(self):
509        """Reset all the options to the options in settings-dist.xml.
510           @return: None"""
511        shutil.copyfile('settings-dist.xml', 'settings.xml')
512        self.requireRestartDialog()
513        self.options_menu.hide()
514
515    def displayOptions(self):
516        """Display the options menu.
517           @return: None"""
518        self.options_menu.show()
519   
520    def readyAction(self, ready_button):
521        """ Called when the user selects a ready button from the HUD """
522        text = "Used the item from %s" % ready_button
523        self.addAction(text)
524       
525    def createBoxGUI(self, title):
526        """Creates a window to display the contents of a box
527           @type title: string
528           @param title: The title for the window
529           @return: None"""
530        if self.box_container:
531            # if it has already been created, just show it
532            self.box_container.showContainer()
533        else:
534            # otherwise create it then show it
535            data = ["dagger01", "empty", "empty", "empty", "empty",
536                    "empty", "empty", "empty", "empty"]
537            self.box_container = ContainerGUI(self.engine, \
538                                              unicode(title), data)
539            def closeAndDelete():
540                self.hideContainer()
541            events = {'takeAllButton':closeAndDelete,
542                      'closeButton':closeAndDelete}
543            self.box_container.container_gui.mapEvents(events)
544            self.box_container.showContainer()
545
546    def hideContainer(self):
547        """Hide the container box
548           @return: None"""
549        if self.box_container:
550            self.box_container.hideContainer()
551
552    def createExamineBox(self, title, desc):
553        """Create an examine box. It displays some textual description of an
554           object
555           @type title: string
556           @param title: The title of the examine box
557           @type desc: string
558           @param desc: The main body of the examine box
559           @return: None"""
560
561        if self.examine_box is not None:
562            self.examine_box.closePopUp()
563        self.examine_box = ExaminePopup(self.engine, title, desc)
564        self.examine_box.showPopUp()
565
566    def showDialogue(self, npc):
567        """Show the NPC dialogue window
568           @type npc: ???
569           @param npc: the npc that we are having a dialogue with"""
570        dialogue = DialogueGUI(npc)
571        dialogue.initiateDialogue()
Note: See TracBrowser for help on using the repository browser.