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

Revision 313, 22.0 KB checked in by Kaydeth_parpg, 10 years ago (diff)

Ticket #2: Patch by Kaydeth. Updated the rest of the classes in the scripts directory to correct some obvious discrepancies with our coding standard. Still need to go through the common and objects sub directories. comment[s:trac, t:2]

  • 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.save_game_callback
387        events_to_map["loadButton"] = self.load_game_callback
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.save_game_callback
401        menu_events["loadButton"] = self.load_game_callback
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.