all repos — Legends-RPG @ a452513181f1eef80107430ce479b678dc2fa21c

A fantasy mini-RPG built with Python and Pygame.

data/battlegui.py (view raw)

  1"""
  2GUI components for battle states.
  3"""
  4import sys
  5import pygame as pg
  6from . import setup, observer
  7from . import constants as c
  8
  9#Python 2/3 compatibility.
 10if sys.version_info[0] == 2:
 11    range = xrange
 12
 13class InfoBox(object):
 14    """
 15    Info box that describes attack damage and other battle
 16    related information.
 17    """
 18    def __init__(self, game_data, experience, gold):
 19        self.game_data = game_data
 20        self.enemy_damage = 0
 21        self.player_damage = 0
 22        self.state = c.SELECT_ACTION
 23        self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
 24        self.title_font.set_underline(True)
 25        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 18)
 26        self.experience_points = experience
 27        self.gold_earned = gold
 28        self.state_dict = self.make_state_dict()
 29        self.image = self.make_image()
 30        self.rect = self.image.get_rect(bottom=608)
 31        self.item_text_list = self.make_item_text()[1:]
 32        self.magic_text_list = self.make_magic_text()[1:]
 33
 34    def make_state_dict(self):
 35        """
 36        Make dictionary of states Battle info can be in.
 37        """
 38        state_dict   = {c.SELECT_ACTION: 'Select an action.',
 39                        c.SELECT_MAGIC: 'Select a magic spell.',
 40                        c.SELECT_ITEM: 'Select an item.',
 41                        c.SELECT_ENEMY: 'Select an enemy.',
 42                        c.ENEMY_ATTACK: 'Enemy attacks player!',
 43                        c.PLAYER_ATTACK: 'Player attacks enemy! ',
 44                        c.RUN_AWAY: 'RUN AWAY!!!',
 45                        c.ENEMY_DAMAGED: self.enemy_damaged(),
 46                        c.ENEMY_DEAD: 'Enemy killed.',
 47                        c.PLAYER_DAMAGED: self.player_hit(),
 48                        c.DRINK_HEALING_POTION: 'Player healed.',
 49                        c.DRINK_ETHER_POTION: 'Magic Points Increased.',
 50                        c.FIRE_SPELL: 'FIRE BLAST!',
 51                        c.BATTLE_WON: 'Battle won!',
 52                        c.SHOW_EXPERIENCE: self.show_experience(),
 53                        c.LEVEL_UP: self.level_up(),
 54                        c.TWO_ACTIONS: 'Two actions per turn mode is now available.',
 55                        c.SHOW_GOLD: self.show_gold()}
 56
 57        return state_dict
 58
 59    def enemy_damaged(self):
 60        """
 61        Return text of enemy being hit using calculated damage.
 62        """
 63        return "Enemy hit with {} damage.".format(self.enemy_damage)
 64
 65    def make_item_text(self):
 66        """
 67        Make the text for when the player selects items.
 68        """
 69        inventory = self.game_data['player inventory']
 70        allowed_item_list = ['Healing Potion', 'Ether Potion']
 71        title = 'SELECT ITEM'
 72        item_text_list = [title]
 73
 74        for item in allowed_item_list:
 75            if item in inventory:
 76                text = item + ": " + str(inventory[item]['quantity'])
 77                item_text_list.append(text)
 78
 79        item_text_list.append('BACK')
 80
 81        return item_text_list
 82
 83    def make_magic_text(self):
 84        """
 85        Make the text for when the player selects magic.
 86        """
 87        inventory = self.game_data['player inventory']
 88        allowed_item_list = ['Fire Blast', 'Cure']
 89        title = 'SELECT MAGIC SPELL'
 90        magic_text_list = [title]
 91        spell_list = [item for item in inventory if item in allowed_item_list]
 92        magic_text_list.extend(spell_list)
 93        magic_text_list.append('BACK')
 94
 95        return magic_text_list
 96
 97    def make_text_sprites(self, text_list):
 98        """
 99        Make sprites out of text.
100        """
101        sprite_group = pg.sprite.Group()
102
103        for i, text in enumerate(text_list):
104            sprite = pg.sprite.Sprite()
105
106            if i == 0:
107                x = 195
108                y = 10
109                surface = self.title_font.render(text, True, c.NEAR_BLACK)
110                rect = surface.get_rect(x=x, y=y)
111            else:
112                x = 100
113                y = (i * 30) + 20
114                surface = self.font.render(text, True, c.NEAR_BLACK)
115                rect = surface.get_rect(x=x, y=y)
116            sprite.image = surface
117            sprite.rect = rect
118            sprite_group.add(sprite)
119
120        return sprite_group
121
122    def make_image(self):
123        """
124        Make image out of box and message.
125        """
126        image = setup.GFX['shopbox']
127        rect = image.get_rect(bottom=608)
128        surface = pg.Surface(rect.size)
129        surface.set_colorkey(c.BLACK)
130        surface.blit(image, (0, 0))
131
132        if self.state == c.SELECT_ITEM:
133            text_sprites = self.make_text_sprites(self.make_item_text())
134            text_sprites.draw(surface)
135        elif self.state == c.SELECT_MAGIC:
136            text_sprites = self.make_text_sprites(self.make_magic_text())
137            text_sprites.draw(surface)
138        else:
139            text_surface = self.font.render(self.state_dict[self.state], True, c.NEAR_BLACK)
140            text_rect = text_surface.get_rect(x=50, y=50)
141            surface.blit(text_surface, text_rect)
142
143        return surface
144
145    def set_enemy_damage(self, enemy_damage):
146        """
147        Set enemy damage in state dictionary.
148        """
149        self.enemy_damage = enemy_damage
150        self.state_dict[c.ENEMY_DAMAGED] = self.enemy_damaged()
151
152    def set_player_damage(self, player_damage):
153        """
154        Set player damage in state dictionary.
155        """
156        self.player_damage = player_damage
157        self.state_dict[c.PLAYER_DAMAGED] = self.player_hit()
158
159    def player_hit(self):
160        if self.player_damage:
161            return "Player hit with {} damage".format(self.player_damage)
162        else:
163            return "Enemy missed!"
164
165    def update(self):
166        """Updates info box"""
167        self.image = self.make_image()
168
169    def show_experience(self):
170        """
171        Show how much experience the player earned.
172        """
173        return "You earned {} experience points this battle!".format(self.experience_points)
174
175    def show_gold(self):
176        """
177        Show how much gold the player earned.
178        """
179        return "You found {} gold.".format(self.gold_earned)
180
181    def level_up(self):
182        """
183        Return message indicating a level up for player.
184        """
185        return "You leveled up to Level {}!".format(self.game_data['player stats']['Level'])
186
187    def reset_level_up_message(self):
188        self.state_dict[c.LEVEL_UP] = self.level_up()
189
190
191
192class SelectBox(object):
193    """
194    Box to select whether to attack, use item, use magic or run away.
195    """
196    def __init__(self):
197        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
198        self.slots = self.make_slots()
199        self.image = self.make_image()
200        self.rect = self.image.get_rect(bottom=608,
201                                        right=800)
202
203    def make_image(self):
204        """
205        Make the box image for
206        """
207        image = setup.GFX['goldbox']
208        rect = image.get_rect(bottom=608)
209        surface = pg.Surface(rect.size)
210        surface.set_colorkey(c.BLACK)
211        surface.blit(image, (0, 0))
212
213        for text in self.slots:
214            text_surface = self.font.render(text, True, c.NEAR_BLACK)
215            text_rect = text_surface.get_rect(x=self.slots[text]['x'],
216                                              y=self.slots[text]['y'])
217            surface.blit(text_surface, text_rect)
218
219        return surface
220
221    def make_slots(self):
222        """
223        Make the slots that hold the text selections, and locations.
224        """
225        slot_dict = {}
226        selections = ['Attack', 'Items', 'Magic', 'Run']
227
228        for i, text in enumerate(selections):
229            slot_dict[text] = {'x': 150,
230                               'y': (i*34)+10}
231
232        return slot_dict
233
234
235class SelectArrow(object):
236    """Small arrow for menu"""
237    def __init__(self, enemy_pos_list, info_box):
238        self.info_box = info_box
239        self.image = setup.GFX['smallarrow']
240        self.rect = self.image.get_rect()
241        self.state = 'select action'
242        self.state_dict = self.make_state_dict()
243        self.pos_list = self.make_select_action_pos_list()
244        self.index = 0
245        self.rect.topleft = self.pos_list[self.index]
246        self.allow_input = False
247        self.enemy_pos_list = enemy_pos_list
248        self.sound_effect_observer = observer.SoundEffects()
249        self.observers = [self.sound_effect_observer]
250
251    def notify(self, event):
252        """
253        Notify all observers of events.
254        """
255        for observer in self.observers:
256            observer.on_notify(event)
257
258    def make_state_dict(self):
259        """Make state dictionary"""
260        state_dict = {'select action': self.select_action,
261                      'select enemy': self.select_enemy,
262                      'select item': self.select_item,
263                      'select magic': self.select_magic,
264                      'invisible': self.become_invisible_surface}
265
266        return state_dict
267
268    def select_action(self, keys):
269        """
270        Select what action the player should take.
271        """
272        self.pos_list = self.make_select_action_pos_list()
273        if self.index > (len(self.pos_list) - 1):
274            print self.pos_list, self.index
275        self.rect.topleft = self.pos_list[self.index]
276
277        self.check_input(keys)
278
279    def make_select_action_pos_list(self):
280        """
281        Make the list of positions the arrow can be in.
282        """
283        pos_list = []
284
285        for i in range(4):
286            x = 590
287            y = (i * 34) + 472
288            pos_list.append((x, y))
289
290        return pos_list
291
292    def select_enemy(self, keys):
293        """
294        Select what enemy you want to take action on.
295        """
296        self.pos_list = self.enemy_pos_list
297
298        if self.pos_list:
299            pos = self.pos_list[self.index]
300            self.rect.x = pos[0] - 60
301            self.rect.y = pos[1] + 20
302
303        self.check_input(keys)
304
305    def check_input(self, keys):
306        if self.allow_input:
307            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
308                self.notify(c.CLICK)
309                self.index += 1
310                self.allow_input = False
311            elif keys[pg.K_UP] and self.index > 0:
312                self.notify(c.CLICK)
313                self.index -= 1
314                self.allow_input = False
315
316
317        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \
318                and keys[pg.K_RIGHT] == False and keys[pg.K_LEFT] == False:
319            self.allow_input = True
320
321    def select_item(self, keys):
322        """
323        Select item to use.
324        """
325        self.pos_list = self.make_select_item_pos_list()
326
327        pos = self.pos_list[self.index]
328        self.rect.x = pos[0] - 60
329        self.rect.y = pos[1] + 20
330
331        self.check_input(keys)
332
333    def make_select_item_pos_list(self):
334        """
335        Make the coordinates for the arrow for the item select screen.
336        """
337        pos_list = []
338        text_list = self.info_box.make_item_text()
339        text_list = text_list[1:]
340
341        for i in range(len(text_list)):
342            left = 90
343            top = (i * 29) + 488
344            pos_list.append((left, top))
345
346        return pos_list
347
348    def select_magic(self, keys):
349        """
350        Select magic to use.
351        """
352        self.pos_list = self.make_select_magic_pos_list()
353
354        pos = self.pos_list[self.index]
355        self.rect.x = pos[0] - 60
356        self.rect.y = pos[1] + 20
357
358        self.check_input(keys)
359
360    def make_select_magic_pos_list(self):
361        """
362        Make the coordinates for the arrow for the magic select screen.
363        """
364        pos_list = []
365        text_list = self.info_box.make_magic_text()
366        text_list = text_list[1:]
367
368        for i in range(len(text_list)):
369            left = 90
370            top = (i * 29) + 488
371            pos_list.append((left, top))
372
373        return pos_list
374
375
376    def become_invisible_surface(self, *args):
377        """
378        Make image attribute an invisible surface.
379        """
380        self.image = pg.Surface(self.rect.size)
381        self.image.set_colorkey(c.BLACK)
382
383    def become_select_item_state(self):
384        self.index = 0
385        self.state = c.SELECT_ITEM
386
387    def become_select_magic_state(self):
388        self.index = 0
389        self.state = c.SELECT_MAGIC
390
391    def enter_select_action(self):
392        """
393        Assign values for the select action state.
394        """
395        pass
396
397    def enter_select_enemy(self):
398        """
399        Assign values for the select enemy state.
400        """
401        pass
402
403    def update(self, keys):
404        """
405        Update arrow position.
406        """
407        self.image = setup.GFX['smallarrow']
408        state_function = self.state_dict[self.state]
409        state_function(keys)
410
411    def draw(self, surface):
412        """
413        Draw to surface.
414        """
415        surface.blit(self.image, self.rect)
416
417    def remove_pos(self, enemy):
418        enemy_list = self.enemy_pos_list
419        enemy_pos = list(enemy.rect.topleft)
420
421        self.enemy_pos_list = [pos for pos in enemy_list if pos != enemy_pos]
422
423
424class PlayerHealth(object):
425    """
426    Basic health meter for player.
427    """
428    def __init__(self, select_box_rect, game_data):
429        self.health_stats = game_data['player stats']['health']
430        self.magic_stats = game_data['player stats']['magic']
431        self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
432        self.posx = select_box_rect.centerx
433        self.posy = select_box_rect.y - 5
434
435    @property
436    def image(self):
437        """
438        Make the image surface for the player
439        """
440        current_health = str(self.health_stats['current'])
441        max_health = str(self.health_stats['maximum'])
442        if len(current_health) == 2:
443            buffer = '  '
444        elif len(current_health) == 1:
445            buffer = '    '
446        else:
447            buffer = ''
448        health_string = "Health: {}{}/{}".format(buffer, current_health, max_health)
449        health_surface =  self.title_font.render(health_string, True, c.NEAR_BLACK)
450        health_rect = health_surface.get_rect(x=20, y=9)
451
452        current_magic = str(self.magic_stats['current'])
453        if len(current_magic) == 2:
454            buffer = '  '
455        elif len(current_magic) == 1:
456            buffer = '    '
457        else:
458            buffer = ''
459        max_magic = str(self.magic_stats['maximum'])
460        magic_string = "Magic:  {}{}/{}".format(buffer, current_magic, max_magic)
461        magic_surface = self.title_font.render(magic_string, True, c.NEAR_BLACK)
462        magic_rect = magic_surface.get_rect(x=20, top=health_rect.bottom)
463
464        box_surface = setup.GFX['battlestatbox']
465        box_rect = box_surface.get_rect()
466
467        parent_surface = pg.Surface(box_rect.size)
468        parent_surface.blit(box_surface, box_rect)
469        parent_surface.blit(health_surface, health_rect)
470        parent_surface.blit(magic_surface, magic_rect)
471
472        return parent_surface
473
474    @property
475    def rect(self):
476        """
477        Make the rect object for image surface.
478        """
479        return self.image.get_rect(centerx=self.posx, bottom=self.posy)
480
481    def draw(self, surface):
482        """
483        Draw health to surface.
484        """
485        surface.blit(self.image, self.rect)