Mercurial > parpg-core
comparison src/parpg/gui/hud.py @ 0:1fd2201f5c36
Initial commit of parpg-core.
author | M. George Hansen <technopolitica@gmail.com> |
---|---|
date | Sat, 14 May 2011 01:12:35 -0700 |
parents | |
children | d60f1dab8469 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1fd2201f5c36 |
---|---|
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 |