comparison gui/hud.py @ 0:7a89ea5404b1

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