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

Revision 261, 19.3 KB checked in by b0rland_parpg, 10 years ago (diff)

Ticket #78, #80: Patch by superfluid. Moving the hud events and context menu code (and inventory code) from world.py to hud.py

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