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