all repos — Legends-RPG @ 615ae8672bd8ae22eed4bbfc45a4b324bc4eb945

A fantasy mini-RPG built with Python and Pygame.

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)