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

Revision 270, 19.0 KB checked in by orlandov, 10 years ago (diff)

Fix Issue #78

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