data/menugui.py (view raw)
1# -*- coding: utf-8 -*-
2
3"""
4This class controls all the GUI for the player
5menu screen.
6"""
7import pygame as pg
8from . import setup, observer
9from . import constants as c
10from . import tools
11
12
13class SmallArrow(pg.sprite.Sprite):
14 """Small arrow for menu"""
15 def __init__(self, info_box):
16 super(SmallArrow, self).__init__()
17 self.image = setup.GFX['smallarrow']
18 self.rect = self.image.get_rect()
19 self.state = 'selectmenu'
20 self.state_dict = self.make_state_dict()
21 self.slots = info_box.slots
22 self.pos_list = []
23
24 def make_state_dict(self):
25 """Make state dictionary"""
26 state_dict = {'selectmenu': self.navigate_select_menu,
27 'itemsubmenu': self.navigate_item_submenu,
28 'magicsubmenu': self.navigate_magic_submenu}
29
30 return state_dict
31
32 def navigate_select_menu(self, pos_index):
33 """Nav the select menu"""
34 self.pos_list = self.make_select_menu_pos_list()
35 self.rect.topleft = self.pos_list[pos_index]
36
37 def navigate_item_submenu(self, pos_index):
38 """Nav the item submenu"""
39 self.pos_list = self.make_item_menu_pos_list()
40 self.rect.topleft = self.pos_list[pos_index]
41
42 def navigate_magic_submenu(self, pos_index):
43 """Nav the magic submenu"""
44 self.pos_list = self.make_magic_menu_pos_list()
45 self.rect.topleft = self.pos_list[pos_index]
46
47 def make_magic_menu_pos_list(self):
48 """
49 Make the list of possible arrow positions for magic submenu.
50 """
51 pos_list = [(310, 119),
52 (310, 169)]
53
54 return pos_list
55
56 def make_select_menu_pos_list(self):
57 """Make the list of possible arrow positions"""
58 pos_list = []
59
60 for i in range(3):
61 pos = (35, 443 + (i * 45))
62 pos_list.append(pos)
63
64 return pos_list
65
66 def make_item_menu_pos_list(self):
67 """Make the list of arrow positions in the item submenu"""
68 pos_list = [(300, 173),
69 (300, 223),
70 (300, 323),
71 (300, 373),
72 (300, 478),
73 (300, 528),
74 (535, 478),
75 (535, 528)]
76
77 return pos_list
78
79 def update(self, pos_index):
80 """Update arrow position"""
81 state_function = self.state_dict[self.state]
82 state_function(pos_index)
83
84 def draw(self, surface):
85 """Draw to surface"""
86 surface.blit(self.image, self.rect)
87
88
89class QuickStats(pg.sprite.Sprite):
90 def __init__(self, game_data):
91 self.inventory = game_data['player inventory']
92 self.game_data = game_data
93 self.health = game_data['player stats']['health']
94 self.stats = self.game_data['player stats']
95 self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
96 self.small_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 18)
97 self.image, self.rect = self.make_image()
98
99 def make_image(self):
100 """
101 Make the surface for the gold box.
102 """
103 stat_list = ['GOLD', 'health', 'magic']
104 magic_health_list = ['health', 'magic']
105 image = setup.GFX['goldbox']
106 rect = image.get_rect(left=10, top=244)
107
108 surface = pg.Surface(rect.size)
109 surface.set_colorkey(c.BLACK)
110 surface.blit(image, (0, 0))
111
112 for i, stat in enumerate(stat_list):
113 first_letter = stat[0].upper()
114 rest_of_letters = stat[1:]
115 if stat in magic_health_list:
116 current = self.stats[stat]['current']
117 max = self.stats[stat]['maximum']
118 text = "{}{}: {}/{}".format(first_letter, rest_of_letters, current, max)
119 elif stat == 'GOLD':
120 text = "Gold: {}".format(self.inventory[stat]['quantity'])
121 render = self.small_font.render(text, True, c.NEAR_BLACK)
122 x = 26
123 y = 45 + (i*30)
124 text_rect = render.get_rect(x=x,
125 centery=y)
126 surface.blit(render, text_rect)
127
128 return surface, rect
129
130 def update(self):
131 """
132 Update gold.
133 """
134 self.image, self.rect = self.make_image()
135
136 def draw(self, surface):
137 """
138 Draw to surface.
139 """
140 surface.blit(self.image, self.rect)
141
142
143class InfoBox(pg.sprite.Sprite):
144 def __init__(self, inventory, player_stats):
145 super(InfoBox, self).__init__()
146 self.inventory = inventory
147 self.player_stats = player_stats
148 self.attack_power = self.get_attack_power()
149 self.defense_power = self.get_defense_power()
150 self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
151 self.big_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 24)
152 self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 28)
153 self.title_font.set_underline(True)
154 self.get_tile = tools.get_tile
155 self.sword = self.get_tile(48, 0, setup.GFX['shopsigns'], 16, 16, 2)
156 self.shield = self.get_tile(32, 0, setup.GFX['shopsigns'], 16, 16, 2)
157 self.potion = self.get_tile(16, 0, setup.GFX['shopsigns'], 16, 16, 2)
158 self.possible_potions = ['Healing Potion', 'ELIXIR', 'Ether Potion']
159 self.possible_armor = ['Wooden Shield', 'Chain Mail']
160 self.possible_weapons = ['Long Sword', 'Rapier']
161 self.possible_magic = ['Fire Blast', 'Cure']
162 self.quantity_items = ['Healing Potion', 'ELIXIR', 'Ether Potion']
163 self.slots = {}
164 self.state = 'stats'
165 self.state_dict = self.make_state_dict()
166 self.print_slots = True
167
168 def get_attack_power(self):
169 """
170 Calculate the current attack power based on equipped weapons.
171 """
172 weapon = self.inventory['equipped weapon']
173 weapon_power = self.inventory[weapon]['power']
174 return weapon_power + (self.player_stats['Level'] * 5)
175
176 def get_defense_power(self):
177 """
178 Calculate the current defense power based on equipped weapons.
179 """
180 defense_power = 0
181 for armor in self.inventory['equipped armor']:
182 defense_power += self.inventory[armor]['power']
183
184 return defense_power
185
186 def make_state_dict(self):
187 """Make the dictionary of state methods"""
188 state_dict = {'stats': self.show_player_stats,
189 'items': self.show_items,
190 'magic': self.show_magic}
191
192 return state_dict
193
194
195 def show_player_stats(self):
196 """Show the player's main stats"""
197 title = 'STATS'
198 stat_list = ['Level', 'experience to next level',
199 'health', 'magic', 'Attack Power',
200 'Defense Power', 'gold']
201 attack_power = 5
202 surface, rect = self.make_blank_info_box(title)
203
204 for i, stat in enumerate(stat_list):
205 if stat == 'health' or stat == 'magic':
206 text = "{}{}: {} / {}".format(stat[0].upper(),
207 stat[1:],
208 str(self.player_stats[stat]['current']),
209 str(self.player_stats[stat]['maximum']))
210 elif stat == 'experience to next level':
211 text = "{}{}: {}".format(stat[0].upper(),
212 stat[1:],
213 self.player_stats[stat])
214 elif stat == 'Attack Power':
215 text = "{}: {}".format(stat, self.get_attack_power())
216 elif stat == 'Defense Power':
217 text = "{}: {}".format(stat, self.get_defense_power())
218 elif stat == 'gold':
219 text = "Gold: {}".format(self.inventory['GOLD']['quantity'])
220 else:
221 text = "{}: {}".format(stat, str(self.player_stats[stat]))
222 text_image = self.font.render(text, True, c.NEAR_BLACK)
223 text_rect = text_image.get_rect(x=50, y=80+(i*50))
224 surface.blit(text_image, text_rect)
225
226 self.image = surface
227 self.rect = rect
228
229
230 def show_items(self):
231 """Show list of items the player has"""
232 title = 'ITEMS'
233 potions = ['POTIONS']
234 weapons = ['WEAPONS']
235 armor = ['ARMOR']
236 for i, item in enumerate(self.inventory):
237 if item in self.possible_weapons:
238 if item == self.inventory['equipped weapon']:
239 item += " (E)"
240 weapons.append(item)
241 elif item in self.possible_armor:
242 if item in self.inventory['equipped armor']:
243 item += " (E)"
244 armor.append(item)
245 elif item in self.possible_potions:
246 potions.append(item)
247
248 self.slots = {}
249 self.assign_slots(weapons, 85)
250 self.assign_slots(armor, 235)
251 self.assign_slots(potions, 390)
252
253 surface, rect = self.make_blank_info_box(title)
254
255 self.blit_item_lists(surface)
256
257 self.sword['rect'].topleft = 40, 80
258 self.shield['rect'].topleft = 40, 230
259 self.potion['rect'].topleft = 40, 385
260 surface.blit(self.sword['surface'], self.sword['rect'])
261 surface.blit(self.shield['surface'], self.shield['rect'])
262 surface.blit(self.potion['surface'], self.potion['rect'])
263
264 self.image = surface
265 self.rect = rect
266
267
268 def assign_slots(self, item_list, starty, weapon_or_armor=False):
269 """Assign each item to a slot in the menu"""
270 if len(item_list) > 3:
271 for i, item in enumerate(item_list[:3]):
272 posx = 80
273 posy = starty + (i * 50)
274 self.slots[(posx, posy)] = item
275 for i, item in enumerate(item_list[3:]):
276 posx = 315
277 posy = (starty + 50) + (i * 5)
278 self.slots[(posx, posy)] = item
279 else:
280 for i, item in enumerate(item_list):
281 posx = 80
282 posy = starty + (i * 50)
283 self.slots[(posx, posy)] = item
284
285 def assign_magic_slots(self, magic_list, starty):
286 """
287 Assign each magic spell to a slot in the menu.
288 """
289 for i, spell in enumerate(magic_list):
290 posx = 120
291 posy = starty + (i * 50)
292 self.slots[(posx, posy)] = spell
293
294 def blit_item_lists(self, surface):
295 """Blit item list to info box surface"""
296 for coord in self.slots:
297 item = self.slots[coord]
298
299 if item in self.possible_potions:
300 text = "{}: {}".format(self.slots[coord],
301 self.inventory[item]['quantity'])
302 else:
303 text = "{}".format(self.slots[coord])
304 text_image = self.font.render(text, True, c.NEAR_BLACK)
305 text_rect = text_image.get_rect(topleft=coord)
306 surface.blit(text_image, text_rect)
307
308 def show_magic(self):
309 """Show list of magic spells the player knows"""
310 title = 'MAGIC'
311 item_list = []
312 for item in self.inventory:
313 if item in self.possible_magic:
314 item_list.append(item)
315 item_list = sorted(item_list)
316
317 self.slots = {}
318 self.assign_magic_slots(item_list, 80)
319
320 surface, rect = self.make_blank_info_box(title)
321
322 for i, item in enumerate(item_list):
323 text_image = self.font.render(item, True, c.NEAR_BLACK)
324 text_rect = text_image.get_rect(x=100, y=80+(i*50))
325 surface.blit(text_image, text_rect)
326
327 self.image = surface
328 self.rect = rect
329
330
331 def make_blank_info_box(self, title):
332 """Make an info box with title, otherwise blank"""
333 image = setup.GFX['playerstatsbox']
334 rect = image.get_rect(left=285, top=35)
335 centerx = rect.width / 2
336
337 surface = pg.Surface(rect.size)
338 surface.set_colorkey(c.BLACK)
339 surface.blit(image, (0,0))
340
341 title_image = self.title_font.render(title, True, c.NEAR_BLACK)
342 title_rect = title_image.get_rect(centerx=centerx, y=30)
343 surface.blit(title_image, title_rect)
344
345 return surface, rect
346
347
348 def update(self):
349 state_function = self.state_dict[self.state]
350 state_function()
351
352
353 def draw(self, surface):
354 """Draw to surface"""
355 surface.blit(self.image, self.rect)
356
357
358class SelectionBox(pg.sprite.Sprite):
359 def __init__(self):
360 self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
361 self.image, self.rect = self.make_image()
362
363 def make_image(self):
364 choices = ['Stats', 'Items', 'Magic']
365 image = setup.GFX['goldbox']
366 rect = image.get_rect(left=10, top=425)
367
368 surface = pg.Surface(rect.size)
369 surface.set_colorkey(c.BLACK)
370 surface.blit(image, (0, 0))
371
372 for i, choice in enumerate(choices):
373 choice_image = self.font.render(choice, True, c.NEAR_BLACK)
374 choice_rect = choice_image.get_rect(x=100, y=(15 + (i * 45)))
375 surface.blit(choice_image, choice_rect)
376
377 return surface, rect
378
379 def draw(self, surface):
380 """Draw to surface"""
381 surface.blit(self.image, self.rect)
382
383
384class MenuGui(object):
385 def __init__(self, level, inventory, stats):
386 self.level = level
387 self.game_data = self.level.game_data
388 self.sfx_observer = observer.SoundEffects()
389 self.observers = [self.sfx_observer]
390 self.inventory = inventory
391 self.stats = stats
392 self.info_box = InfoBox(inventory, stats)
393 self.gold_box = QuickStats(self.game_data)
394 self.selection_box = SelectionBox()
395 self.arrow = SmallArrow(self.info_box)
396 self.arrow_index = 0
397 self.allow_input = False
398
399
400
401 def check_for_input(self, keys):
402 """Check for input"""
403 if self.allow_input:
404 if keys[pg.K_DOWN]:
405 if self.arrow_index < len(self.arrow.pos_list) - 1:
406 self.notify(c.CLICK)
407 self.arrow_index += 1
408 self.allow_input = False
409 elif keys[pg.K_UP]:
410 if self.arrow_index > 0:
411 self.notify(c.CLICK)
412 self.arrow_index -= 1
413 self.allow_input = False
414 elif keys[pg.K_RIGHT]:
415 if self.info_box.state == 'items':
416 if not self.arrow.state == 'itemsubmenu':
417 self.notify(c.CLICK)
418 self.arrow_index = 0
419 self.arrow.state = 'itemsubmenu'
420 elif self.info_box.state == 'magic':
421 if not self.arrow.state == 'magicsubmenu':
422 self.notify(c.CLICK)
423 self.arrow_index = 0
424 self.arrow.state = 'magicsubmenu'
425 self.allow_input = False
426
427 elif keys[pg.K_LEFT]:
428 self.notify(c.CLICK)
429 self.arrow.state = 'selectmenu'
430 self.arrow_index = 0
431 self.allow_input = False
432 elif keys[pg.K_SPACE]:
433 self.notify(c.CLICK2)
434 if self.arrow.state == 'selectmenu':
435 if self.arrow_index == 0:
436 self.info_box.state = 'stats'
437 elif self.arrow_index == 1:
438 self.info_box.state = 'items'
439 elif self.arrow_index == 2:
440 self.info_box.state = 'magic'
441 elif self.arrow.state == 'itemsubmenu':
442 self.select_item()
443 elif self.arrow.state == 'magicsubmenu':
444 self.select_magic()
445
446 self.allow_input = False
447 elif keys[pg.K_RETURN]:
448 self.level.state = 'normal'
449 self.info_box.state = 'stats'
450 self.allow_input = False
451 self.arrow_index = 0
452 self.arrow.state = 'selectmenu'
453
454 if (not keys[pg.K_DOWN]
455 and not keys[pg.K_UP]
456 and not keys[pg.K_RETURN]
457 and not keys[pg.K_SPACE]
458 and not keys[pg.K_RIGHT]
459 and not keys[pg.K_LEFT]):
460 self.allow_input = True
461
462 def notify(self, event):
463 """
464 Notify all observers of event.
465 """
466 for observer in self.observers:
467 observer.on_notify(event)
468
469 def select_item(self):
470 """
471 Select item from item menu.
472 """
473 health = self.game_data['player stats']['health']
474 posx = self.arrow.rect.x - 220
475 posy = self.arrow.rect.y - 38
476
477 if (posx, posy) in self.info_box.slots:
478 if self.info_box.slots[(posx, posy)][:7] == 'Healing':
479 potion = 'Healing Potion'
480 value = 30
481 self.drink_potion(potion, health, value)
482 elif self.info_box.slots[(posx, posy)][:5] == 'Ether':
483 potion = 'Ether Potion'
484 stat = self.game_data['player stats']['magic']
485 value = 30
486 self.drink_potion(potion, stat, value)
487 elif self.info_box.slots[(posx, posy)][:10] == 'Long Sword':
488 self.inventory['equipped weapon'] = 'Long Sword'
489 elif self.info_box.slots[(posx, posy)][:6] == 'Rapier':
490 self.inventory['equipped weapon'] = 'Rapier'
491 elif self.info_box.slots[(posx, posy)][:13] == 'Wooden Shield':
492 if 'Wooden Shield' in self.inventory['equipped armor']:
493 self.inventory['equipped armor'].remove('Wooden Shield')
494 else:
495 self.inventory['equipped armor'].append('Wooden Shield')
496 elif self.info_box.slots[(posx, posy)][:10] == 'Chain Mail':
497 if 'Chain Mail' in self.inventory['equipped armor']:
498 self.inventory['equipped armor'].remove('Chain Mail')
499 else:
500 self.inventory['equipped armor'].append('Chain Mail')
501
502 def select_magic(self):
503 """
504 Select spell from magic menu.
505 """
506 health = self.game_data['player stats']['health']
507 magic = self.game_data['player stats']['magic']
508 posx = self.arrow.rect.x - 190
509 posy = self.arrow.rect.y - 39
510
511 if (posx, posy) in self.info_box.slots:
512 if self.info_box.slots[(posx, posy)][:4] == 'Cure':
513 self.use_cure_spell()
514
515 def use_cure_spell(self):
516 """
517 Use cure spell to heal player.
518 """
519 health = self.game_data['player stats']['health']
520 magic = self.game_data['player stats']['magic']
521 inventory = self.game_data['player inventory']
522
523 if magic['current'] >= inventory['Cure']['magic points']:
524 magic['current'] -= inventory['Cure']['magic points']
525 health['current'] += inventory['Cure']['power']
526 if health['current'] > health['maximum']:
527 health['current'] = health['maximum']
528
529 def drink_potion(self, potion, stat, value):
530 """
531 Drink potion and change player stats.
532 """
533 self.inventory[potion]['quantity'] -= 1
534 stat['current'] += value
535 if stat['current'] > stat['maximum']:
536 stat['current'] = stat['maximum']
537 if not self.inventory[potion]['quantity']:
538 del self.inventory[potion]
539
540 def update(self, keys):
541 self.info_box.update()
542 self.gold_box.update()
543 self.arrow.update(self.arrow_index)
544 self.check_for_input(keys)
545
546
547 def draw(self, surface):
548 self.gold_box.draw(surface)
549 self.info_box.draw(surface)
550 self.selection_box.draw(surface)
551 self.arrow.draw(surface)