all repos — Legends-RPG @ bc9dbbaff0f24475c11c5f6a992e77532b9db02c

A fantasy mini-RPG built with Python and Pygame.

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)